```
feat: add market stats endpoint, wallet integration, and browser wallet link - Update devnet genesis timestamp to 1767000206 - Add market statistics endpoint with 24h volume, price change, and payment counts - Add wallet balance and info API endpoints in exchange router - Remove unused SessionDep dependencies from exchange endpoints - Integrate real AITBC wallet extension connection in trade-exchange UI - Add market data fetching with API fallback for price and volume display - Add cache-busting query
This commit is contained in:
112
extensions/aitbc-wallet/README.md
Normal file
112
extensions/aitbc-wallet/README.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# AITBC Browser Wallet Extension
|
||||
|
||||
A browser extension that provides AITBC wallet functionality for interacting with the AITBC Trade Exchange and other dApps.
|
||||
|
||||
## Features
|
||||
|
||||
- **Wallet Management**: Create new accounts or import existing private keys
|
||||
- **Secure Storage**: Private keys are stored locally in the browser
|
||||
- **dApp Integration**: Connect to AITBC Trade Exchange and other supported dApps
|
||||
- **Transaction Signing**: Sign transactions and messages securely
|
||||
- **Balance Tracking**: View your AITBC token balance
|
||||
|
||||
## Installation
|
||||
|
||||
### Development Installation
|
||||
|
||||
1. Clone this repository
|
||||
2. Open Chrome and navigate to `chrome://extensions/`
|
||||
3. Enable "Developer mode" in the top right
|
||||
4. Click "Load unpacked"
|
||||
5. Select the `aitbc-wallet` folder
|
||||
|
||||
### Production Installation
|
||||
|
||||
The extension will be published to the Chrome Web Store. Installation instructions will be available once published.
|
||||
|
||||
## Usage
|
||||
|
||||
### Connecting to the Exchange
|
||||
|
||||
1. Install the AITBC Wallet extension
|
||||
2. Navigate to https://aitbc.bubuit.net/Exchange
|
||||
3. Toggle the switch from "Demo Mode" to "Real Mode"
|
||||
4. Click "Connect AITBC Wallet"
|
||||
5. Approve the connection request in the popup
|
||||
|
||||
### Managing Accounts
|
||||
|
||||
1. Click the AITBC Wallet icon in your browser toolbar
|
||||
2. Use "Create New Account" to generate a new wallet
|
||||
3. Use "Import Private Key" to restore an existing wallet
|
||||
4. **Important**: Save your private key securely! It cannot be recovered if lost.
|
||||
|
||||
## API Reference
|
||||
|
||||
The extension injects a `window.aitbcWallet` object into supported dApps with the following methods:
|
||||
|
||||
### `aitbcWallet.connect()`
|
||||
Connect the dApp to the wallet.
|
||||
```javascript
|
||||
const response = await aitbcWallet.connect();
|
||||
console.log(response.address); // User's AITBC address
|
||||
```
|
||||
|
||||
### `aitbcWallet.getAccount()`
|
||||
Get the current account address.
|
||||
```javascript
|
||||
const address = await aitbcWallet.getAccount();
|
||||
```
|
||||
|
||||
### `aitbcWallet.getBalance(address)`
|
||||
Get the AITBC balance for an address.
|
||||
```javascript
|
||||
const balance = await aitbcWallet.getBalance('aitbc1...');
|
||||
console.log(balance.amount); // Balance in AITBC
|
||||
```
|
||||
|
||||
### `aitbcWallet.sendTransaction(to, amount, data)`
|
||||
Send AITBC tokens to another address.
|
||||
```javascript
|
||||
const tx = await aitbcWallet.sendTransaction('aitbc1...', 100);
|
||||
console.log(tx.hash); // Transaction hash
|
||||
```
|
||||
|
||||
### `aitbcWallet.signMessage(message)`
|
||||
Sign a message with the private key.
|
||||
```javascript
|
||||
const signature = await aitbcWallet.signMessage('Hello AITBC!');
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- Private keys are stored locally in Chrome's storage
|
||||
- Always verify you're on the correct domain before connecting
|
||||
- Never share your private key with anyone
|
||||
- Keep your browser and extension updated
|
||||
|
||||
## Development
|
||||
|
||||
To modify the extension:
|
||||
|
||||
1. Make changes to the source files
|
||||
2. Go to `chrome://extensions/`
|
||||
3. Click the refresh button on the AITBC Wallet card
|
||||
4. Test your changes
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
aitbc-wallet/
|
||||
├── manifest.json # Extension configuration
|
||||
├── content.js # Content script for dApp communication
|
||||
├── injected.js # Script injected into dApps
|
||||
├── popup.html # Extension popup UI
|
||||
├── popup.js # Popup logic
|
||||
├── icons/ # Extension icons
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
For issues or feature requests, please create an issue in the repository.
|
||||
28
extensions/aitbc-wallet/content.js
Normal file
28
extensions/aitbc-wallet/content.js
Normal file
@@ -0,0 +1,28 @@
|
||||
// Content script for AITBC Wallet extension
|
||||
(function() {
|
||||
// Inject the wallet API into the page
|
||||
const script = document.createElement('script');
|
||||
script.src = chrome.runtime.getURL('injected.js');
|
||||
script.onload = function() {
|
||||
this.remove();
|
||||
};
|
||||
(document.head || document.documentElement).appendChild(script);
|
||||
|
||||
// Listen for messages from the injected script
|
||||
window.addEventListener('message', function(event) {
|
||||
// Only accept messages from our own window
|
||||
if (event.source !== window) return;
|
||||
|
||||
if (event.data.type && event.data.type === 'AITBC_WALLET_REQUEST') {
|
||||
// Forward the request to the background script
|
||||
chrome.runtime.sendMessage(event.data, function(response) {
|
||||
// Send the response back to the page
|
||||
window.postMessage({
|
||||
type: 'AITBC_WALLET_RESPONSE',
|
||||
id: event.data.id,
|
||||
response: response
|
||||
}, '*');
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
106
extensions/aitbc-wallet/injected.js
Normal file
106
extensions/aitbc-wallet/injected.js
Normal file
@@ -0,0 +1,106 @@
|
||||
// Injected script that provides the AITBC wallet API to the dApp
|
||||
(function() {
|
||||
// Create the wallet API object
|
||||
const aitbcWallet = {
|
||||
// Check if wallet is available
|
||||
isAvailable: function() {
|
||||
return true;
|
||||
},
|
||||
|
||||
// Connect to wallet
|
||||
connect: async function() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const requestId = Date.now().toString();
|
||||
|
||||
// Send request to content script
|
||||
window.postMessage({
|
||||
type: 'AITBC_WALLET_REQUEST',
|
||||
id: requestId,
|
||||
method: 'connect'
|
||||
}, '*');
|
||||
|
||||
// Listen for response
|
||||
const messageHandler = function(event) {
|
||||
if (event.data.type === 'AITBC_WALLET_RESPONSE' && event.data.id === requestId) {
|
||||
window.removeEventListener('message', messageHandler);
|
||||
if (event.data.response.error) {
|
||||
reject(new Error(event.data.response.error));
|
||||
} else {
|
||||
resolve(event.data.response);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('message', messageHandler);
|
||||
|
||||
// Timeout after 30 seconds
|
||||
setTimeout(() => {
|
||||
window.removeEventListener('message', messageHandler);
|
||||
reject(new Error('Connection timeout'));
|
||||
}, 30000);
|
||||
});
|
||||
},
|
||||
|
||||
// Get account address
|
||||
getAccount: async function() {
|
||||
const accounts = await this.request({ method: 'accounts' });
|
||||
return accounts[0];
|
||||
},
|
||||
|
||||
// Get balance
|
||||
getBalance: async function(address) {
|
||||
return this.request({ method: 'getBalance', params: { address } });
|
||||
},
|
||||
|
||||
// Send transaction
|
||||
sendTransaction: async function(to, amount, data = null) {
|
||||
return this.request({
|
||||
method: 'sendTransaction',
|
||||
params: { to, amount, data }
|
||||
});
|
||||
},
|
||||
|
||||
// Sign message
|
||||
signMessage: async function(message) {
|
||||
return this.request({ method: 'signMessage', params: { message } });
|
||||
},
|
||||
|
||||
// Generic request method
|
||||
request: async function(payload) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const requestId = Date.now().toString();
|
||||
|
||||
window.postMessage({
|
||||
type: 'AITBC_WALLET_REQUEST',
|
||||
id: requestId,
|
||||
method: payload.method,
|
||||
params: payload.params || {}
|
||||
}, '*');
|
||||
|
||||
const messageHandler = function(event) {
|
||||
if (event.data.type === 'AITBC_WALLET_RESPONSE' && event.data.id === requestId) {
|
||||
window.removeEventListener('message', messageHandler);
|
||||
if (event.data.response.error) {
|
||||
reject(new Error(event.data.response.error));
|
||||
} else {
|
||||
resolve(event.data.response);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('message', messageHandler);
|
||||
|
||||
setTimeout(() => {
|
||||
window.removeEventListener('message', messageHandler);
|
||||
reject(new Error('Request timeout'));
|
||||
}, 30000);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Inject the wallet API into the window object
|
||||
window.aitbcWallet = aitbcWallet;
|
||||
|
||||
// Fire an event to notify the dApp that the wallet is ready
|
||||
window.dispatchEvent(new Event('aitbcWalletReady'));
|
||||
})();
|
||||
32
extensions/aitbc-wallet/manifest.json
Normal file
32
extensions/aitbc-wallet/manifest.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "AITBC Wallet",
|
||||
"version": "1.0.0",
|
||||
"description": "AITBC Browser Wallet for trading and managing AITBC tokens",
|
||||
"permissions": [
|
||||
"storage",
|
||||
"activeTab"
|
||||
],
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["https://aitbc.bubuit.net/*", "http://localhost:3002/*"],
|
||||
"js": ["content.js"],
|
||||
"run_at": "document_start"
|
||||
}
|
||||
],
|
||||
"action": {
|
||||
"default_popup": "popup.html",
|
||||
"default_title": "AITBC Wallet"
|
||||
},
|
||||
"web_accessible_resources": [
|
||||
{
|
||||
"resources": ["injected.js"],
|
||||
"matches": ["https://aitbc.bubuit.net/*", "http://localhost:3002/*"]
|
||||
}
|
||||
],
|
||||
"icons": {
|
||||
"16": "icons/icon-16.png",
|
||||
"48": "icons/icon-48.png",
|
||||
"128": "icons/icon-128.png"
|
||||
}
|
||||
}
|
||||
109
extensions/aitbc-wallet/popup.html
Normal file
109
extensions/aitbc-wallet/popup.html
Normal file
@@ -0,0 +1,109 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
body {
|
||||
width: 350px;
|
||||
padding: 20px;
|
||||
font-family: Arial, sans-serif;
|
||||
background: linear-gradient(135deg, #f97316 0%, #ea580c 100%);
|
||||
color: white;
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.logo {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: 10px;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
color: #f97316;
|
||||
}
|
||||
.wallet-info {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.address {
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
word-break: break-all;
|
||||
margin: 5px 0;
|
||||
}
|
||||
.balance {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin: 10px 0;
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
button {
|
||||
background: white;
|
||||
color: #f97316;
|
||||
border: none;
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
button:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.transactions {
|
||||
margin-top: 20px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.tx-item {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<div class="logo">AITBC</div>
|
||||
<h1>Wallet</h1>
|
||||
</div>
|
||||
|
||||
<div class="wallet-info">
|
||||
<div>Account Address:</div>
|
||||
<div class="address" id="accountAddress">Not connected</div>
|
||||
<div class="balance" id="balance">0 AITBC</div>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button onclick="createAccount()">Create New Account</button>
|
||||
<button onclick="importAccount()">Import Private Key</button>
|
||||
<button onclick="sendTokens()">Send Tokens</button>
|
||||
<button onclick="receiveTokens()">Receive Tokens</button>
|
||||
<button onclick="viewOnExplorer()">View on Explorer</button>
|
||||
</div>
|
||||
|
||||
<div class="transactions">
|
||||
<h3>Recent Transactions</h3>
|
||||
<div id="transactionList">
|
||||
<div class="tx-item">No transactions yet</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="popup.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
162
extensions/aitbc-wallet/popup.js
Normal file
162
extensions/aitbc-wallet/popup.js
Normal file
@@ -0,0 +1,162 @@
|
||||
// Popup script for AITBC Wallet extension
|
||||
let currentAccount = null;
|
||||
let accounts = [];
|
||||
|
||||
// Load wallet data on popup open
|
||||
document.addEventListener('DOMContentLoaded', async function() {
|
||||
await loadWalletData();
|
||||
updateUI();
|
||||
});
|
||||
|
||||
// Load wallet data from storage
|
||||
async function loadWalletData() {
|
||||
const result = await chrome.storage.local.get(['accounts', 'currentAccount']);
|
||||
accounts = result.accounts || [];
|
||||
currentAccount = result.currentAccount || null;
|
||||
}
|
||||
|
||||
// Save wallet data to storage
|
||||
async function saveWalletData() {
|
||||
await chrome.storage.local.set({
|
||||
accounts: accounts,
|
||||
currentAccount: currentAccount
|
||||
});
|
||||
}
|
||||
|
||||
// Update UI with current wallet state
|
||||
function updateUI() {
|
||||
const addressEl = document.getElementById('accountAddress');
|
||||
const balanceEl = document.getElementById('balance');
|
||||
|
||||
if (currentAccount) {
|
||||
addressEl.textContent = currentAccount.address;
|
||||
balanceEl.textContent = `${currentAccount.balance || 0} AITBC`;
|
||||
} else {
|
||||
addressEl.textContent = 'Not connected';
|
||||
balanceEl.textContent = '0 AITBC';
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new account
|
||||
async function createAccount() {
|
||||
// Generate a new private key and address
|
||||
const privateKey = generatePrivateKey();
|
||||
const address = await generateAddress(privateKey);
|
||||
|
||||
const newAccount = {
|
||||
address: address,
|
||||
privateKey: privateKey,
|
||||
balance: 0,
|
||||
created: new Date().toISOString()
|
||||
};
|
||||
|
||||
accounts.push(newAccount);
|
||||
currentAccount = newAccount;
|
||||
await saveWalletData();
|
||||
updateUI();
|
||||
|
||||
alert('New account created! Please save your private key securely.');
|
||||
}
|
||||
|
||||
// Import account from private key
|
||||
async function importAccount() {
|
||||
const privateKey = prompt('Enter your private key:');
|
||||
if (!privateKey) return;
|
||||
|
||||
try {
|
||||
const address = await generateAddress(privateKey);
|
||||
|
||||
// Check if account already exists
|
||||
const existing = accounts.find(a => a.address === address);
|
||||
if (existing) {
|
||||
currentAccount = existing;
|
||||
} else {
|
||||
currentAccount = {
|
||||
address: address,
|
||||
privateKey: privateKey,
|
||||
balance: 0,
|
||||
created: new Date().toISOString()
|
||||
};
|
||||
accounts.push(currentAccount);
|
||||
}
|
||||
|
||||
await saveWalletData();
|
||||
updateUI();
|
||||
alert('Account imported successfully!');
|
||||
} catch (error) {
|
||||
alert('Invalid private key!');
|
||||
}
|
||||
}
|
||||
|
||||
// Send tokens
|
||||
async function sendTokens() {
|
||||
if (!currentAccount) {
|
||||
alert('Please create or import an account first!');
|
||||
return;
|
||||
}
|
||||
|
||||
const to = prompt('Send to address:');
|
||||
const amount = prompt('Amount:');
|
||||
|
||||
if (!to || !amount) return;
|
||||
|
||||
// In a real implementation, this would create and sign a transaction
|
||||
alert(`Would send ${amount} AITBC to ${to}`);
|
||||
}
|
||||
|
||||
// Receive tokens
|
||||
function receiveTokens() {
|
||||
if (!currentAccount) {
|
||||
alert('Please create or import an account first!');
|
||||
return;
|
||||
}
|
||||
|
||||
alert(`Your receiving address:\n${currentAccount.address}`);
|
||||
}
|
||||
|
||||
// View on explorer
|
||||
function viewOnExplorer() {
|
||||
if (!currentAccount) {
|
||||
alert('Please create or import an account first!');
|
||||
return;
|
||||
}
|
||||
|
||||
chrome.tabs.create({ url: `https://aitbc.bubuit.net/explorer/address/${currentAccount.address}` });
|
||||
}
|
||||
|
||||
// Generate a random private key (demo only)
|
||||
function generatePrivateKey() {
|
||||
const array = new Uint8Array(32);
|
||||
crypto.getRandomValues(array);
|
||||
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
|
||||
}
|
||||
|
||||
// Generate address from private key (demo only)
|
||||
async function generateAddress(privateKey) {
|
||||
// In a real implementation, this would derive the address from the private key
|
||||
// using the appropriate cryptographic algorithm
|
||||
const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(privateKey));
|
||||
return 'aitbc1' + Array.from(new Uint8Array(hash), b => b.toString(16).padStart(2, '0')).join('').substring(0, 40);
|
||||
}
|
||||
|
||||
// Listen for connection requests from dApps
|
||||
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
||||
if (request.method === 'connect') {
|
||||
// Show connection dialog
|
||||
const connected = confirm(`Allow this site to connect to your AITBC Wallet?`);
|
||||
|
||||
if (connected && currentAccount) {
|
||||
sendResponse({
|
||||
success: true,
|
||||
address: currentAccount.address
|
||||
});
|
||||
} else {
|
||||
sendResponse({
|
||||
success: false,
|
||||
error: 'User rejected connection'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return true; // Keep the message channel open for async response
|
||||
});
|
||||
Reference in New Issue
Block a user