Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 164 additions & 0 deletions rtc-balance-extension/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# RTC Balance Viewer - Browser Extension

A lightweight browser extension that displays RTC (RustChain Token) balance for a configured wallet or miner ID.

## Features

- **Minimal UI**: Clean, simple interface showing your RTC balance at a glance
- **Configurable Endpoint**: Set your own RPC/API endpoint for balance queries
- **Auto-refresh**: Automatic balance updates at configurable intervals
- **Wallet/Miner ID Support**: Works with any wallet or miner ID format
- **Privacy-focused**: All data stored locally in browser storage

## Installation

### Chrome / Chromium / Edge

1. Open your browser and navigate to `chrome://extensions/`
2. Enable **Developer mode** (toggle in the top right corner)
3. Click **Load unpacked**
4. Select the `rtc-balance-extension` folder
5. The extension icon will appear in your browser toolbar

### Firefox

1. Open Firefox and navigate to `about:debugging#/runtime/this-firefox`
2. Click **Load Temporary Add-on**
3. Select the `manifest.json` file in the `rtc-balance-extension` folder
4. The extension will be loaded until Firefox is restarted

> **Note**: For Firefox, you may need to adjust the manifest version or use WebExtensions API compatibility layer.

## Configuration

1. Click the extension icon in your browser toolbar
2. Click **Settings** button
3. Enter your **Wallet/Miner ID**
4. Enter your **API Endpoint** URL
5. Set **Auto-refresh interval** (1-60 minutes)
6. Click **Save**

### API Endpoint Format

The extension supports various endpoint formats:

- `https://api.rustchain.io/balance` - Wallet ID appended automatically
- `https://api.rustchain.io/balance/{walletId}` - Include wallet ID in URL
- `https://api.rustchain.io/balance?address={walletId}` - Query parameter format

The extension will automatically adapt to your endpoint format.

## Usage

- **Refresh**: Click the Refresh button to manually update your balance
- **Settings**: Configure wallet ID, endpoint, and refresh interval
- **Balance Display**: Shows current RTC balance with last update time

## File Structure

```
rtc-balance-extension/
├── manifest.json # Extension manifest (MV3)
├── popup.html # Popup UI structure
├── popup.js # Popup logic and event handlers
├── background.js # Background service worker for API calls
├── styles.css # Popup styling
├── icons/ # Extension icons
│ ├── icon16.png
│ ├── icon48.png
│ └── icon128.png
└── README.md # This file
```

## Permissions

The extension requests the following permissions:

- `storage`: Store wallet ID, endpoint, and settings locally
- `alarms`: Enable automatic balance refresh
- `host_permissions`: Allow API calls to configured endpoints

All data is stored locally and never transmitted except to your configured endpoint.

## Development

### Testing Changes

1. Make changes to extension files
2. Go to `chrome://extensions/`
3. Click the refresh icon on the extension card
4. Re-open the popup to see changes

### Building Icons

Placeholder icons should be replaced with actual RTC branding. You can generate icons using:

```bash
# Using ImageMagick to create placeholder icons
mkdir -p icons
convert -size 16x16 xc:#667eea icons/icon16.png
convert -size 48x48 xc:#667eea icons/icon48.png
convert -size 128x128 xc:#667eea icons/icon128.png
```

Or use any image editor to create PNG icons with the desired branding.

## Troubleshooting

### Balance not showing

1. Verify your wallet/miner ID is correct
2. Check that the API endpoint is accessible
3. Open browser DevTools and check the extension console for errors

### Auto-refresh not working

1. Ensure the interval is set to at least 1 minute
2. Check that the extension has necessary permissions
3. Try reloading the extension

### API endpoint errors

The extension expects JSON responses. If your API returns a different format, you may need to:

1. Modify `background.js` to parse your specific response format
2. Use a proxy endpoint that returns compatible JSON

## API Response Format

The extension can parse various JSON response formats:

```json
// Simple number
123.45

// Object with balance field
{ "balance": 123.45 }
{ "data": { "balance": 123.45 } }
{ "result": { "available": "123.45" } }
```

## Security Considerations

- Never share your wallet/miner ID with untrusted parties
- Only use HTTPS endpoints in production
- The extension stores configuration locally in browser storage
- Review the source code before installing

## License

MIT License - See LICENSE file for details.

## Contributing

Contributions are welcome! Please follow these guidelines:

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Test thoroughly
5. Submit a pull request

## Support

For issues or questions, please open an issue in the repository.
156 changes: 156 additions & 0 deletions rtc-balance-extension/background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// Background service worker for RTC Balance Viewer extension

// Initialize alarm on extension install
chrome.runtime.onInstalled.addListener((details) => {
if (details.reason === 'install') {
// Set default refresh interval
chrome.storage.local.set({ refreshInterval: 5 });
}

// Setup alarm for auto-refresh
setupAlarm();
});

// Setup alarm for periodic balance refresh
async function setupAlarm() {
try {
const result = await chrome.storage.local.get(['refreshInterval']);
const interval = result.refreshInterval || 5;

// Clear existing alarm
await chrome.alarms.clear('balanceRefresh');

// Create new alarm (minimum interval is 1 minute for non-Chrome extensions in development)
chrome.alarms.create('balanceRefresh', {
periodInMinutes: Math.max(interval, 1)
});
} catch (error) {
console.error('Error setting up alarm:', error);
}
}

// Handle alarm events
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === 'balanceRefresh') {
// Could trigger a notification here if balance changed significantly
console.log('Auto-refresh triggered');
}
});

// Handle messages from popup
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'fetchBalance') {
fetchBalance(message.endpoint, message.walletId)
.then((result) => sendResponse(result))
.catch((error) => sendResponse({ success: false, error: error.message }));

// Return true to indicate async response
return true;
}

if (message.action === 'updateAlarm') {
setupAlarm();
sendResponse({ success: true });
return true;
}
});

// Fetch balance from API endpoint
async function fetchBalance(endpoint, walletId) {
try {
// Validate endpoint URL
if (!endpoint || !walletId) {
return { success: false, error: 'Missing endpoint or wallet ID' };
}

// Construct the API URL with wallet ID
// Common patterns: /balance/{walletId}, /balance?address={walletId}, etc.
let url = endpoint;

if (!endpoint.includes(walletId)) {
// Try to append wallet ID to URL
if (endpoint.endsWith('/')) {
url = endpoint + walletId;
} else if (endpoint.includes('?')) {
url = endpoint + '&address=' + walletId;
} else {
url = endpoint + '?address=' + walletId;
}
}

const response = await fetch(url, {
method: 'GET',
headers: {
'Accept': 'application/json'
}
});

if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}

const data = await response.json();

// Try to extract balance from various response formats
let balance = extractBalance(data);

if (balance === null) {
return { success: false, error: 'Could not parse balance from response' };
}

return { success: true, balance: balance };
} catch (error) {
console.error('Error fetching balance:', error);
return { success: false, error: error.message };
}
}

// Extract balance from various API response formats
function extractBalance(data) {
if (typeof data === 'number') {
return data;
}

if (typeof data === 'string') {
const parsed = parseFloat(data);
return isNaN(parsed) ? null : parsed;
}

if (typeof data === 'object' && data !== null) {
// Common balance field names
const balanceFields = ['balance', 'available', 'amount', 'value', 'data', 'result'];

for (const field of balanceFields) {
if (data[field] !== undefined) {
if (typeof data[field] === 'number') {
return data[field];
}
if (typeof data[field] === 'string') {
const parsed = parseFloat(data[field]);
if (!isNaN(parsed)) {
return parsed;
}
}
// Handle nested objects
if (typeof data[field] === 'object' && data[field] !== null) {
const nestedBalance = extractBalance(data[field]);
if (nestedBalance !== null) {
return nestedBalance;
}
}
}
}

// Try to find any numeric value in the object
for (const key in data) {
if (typeof data[key] === 'number') {
return data[key];
}
}
}

return null;
}

// Setup alarm when extension starts
setupAlarm();
Loading
Loading