Workflow updating files of stellarwallet

This commit is contained in:
RanchiMall Dev 2026-01-12 13:09:14 +00:00
parent bc162dc331
commit e7da9c24c8
8 changed files with 17777 additions and 0 deletions

1
stellarwallet/README.md Normal file
View File

@ -0,0 +1 @@
# stellarwallet

1986
stellarwallet/index.html Normal file

File diff suppressed because it is too large Load Diff

11473
stellarwallet/lib.stellar.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,509 @@
(function(GLOBAL) {
'use strict';
// Stellar Horizon API endpoints
const HORIZON_URL = 'https://horizon.stellar.org'; // Mainnet
const stellarAPI = {};
let StellarSdk = null;
let server = null;
// Initialize Stellar SDK when available
stellarAPI.init = function() {
if (typeof window !== 'undefined') {
const sdkCandidate = window.StellarSdk || window['stellar-sdk'] || window.StellarBase;
if (sdkCandidate) {
let ServerClass = null;
if (sdkCandidate.Server) {
ServerClass = sdkCandidate.Server;
StellarSdk = sdkCandidate;
} else if (sdkCandidate.Horizon && sdkCandidate.Horizon.Server) {
ServerClass = sdkCandidate.Horizon.Server;
StellarSdk = sdkCandidate; // Store the full SDK object
}
if (ServerClass) {
try {
server = new ServerClass(HORIZON_URL);
return true;
} catch (error) {
console.error('❌ Error creating Server instance:', error);
return false;
}
} else {
console.error('❌ Server class not found in StellarSdk');
return false;
}
} else {
console.error('❌ StellarSdk not found on window');
return false;
}
}
console.warn('⚠️ Window object not available');
return false;
};
stellarAPI.forceInit = function() {
return stellarAPI.init();
};
// Get account balance and info
stellarAPI.getBalance = async function(address) {
try {
const response = await fetch(`${HORIZON_URL}/accounts/${address}`);
if (!response.ok) {
if (response.status === 404) {
throw new Error('Account not found. The account may not be funded yet.');
}
throw new Error(`Failed to fetch balance: ${response.status}`);
}
const data = await response.json();
// Find native XLM balance
const nativeBalance = data.balances.find(b => b.asset_type === 'native');
return {
address: data.account_id,
balance: nativeBalance ? parseFloat(nativeBalance.balance) : 0,
balanceXlm: nativeBalance ? parseFloat(nativeBalance.balance) : 0,
sequence: data.sequence,
subentryCount: data.subentry_count,
numSponsoring: data.num_sponsoring || 0,
numSponsored: data.num_sponsored || 0,
balances: data.balances, // All balances including assets
signers: data.signers,
flags: data.flags,
thresholds: data.thresholds,
lastModifiedLedger: data.last_modified_ledger,
// Minimum balance calculation: (2 + subentry_count) * 0.5 XLM
minBalance: (2 + data.subentry_count) * 0.5
};
} catch (error) {
throw error;
}
};
// Get transaction history with pagination
stellarAPI.getTransactions = async function(address, options = {}) {
const limit = options.limit || 10;
const cursor = options.cursor || options.next || null;
const order = options.order || 'desc'; // desc = newest first
let url = `${HORIZON_URL}/accounts/${address}/transactions?limit=${limit}&order=${order}`;
if (cursor) {
url += `&cursor=${cursor}`;
}
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch transactions: ${response.status}`);
}
const data = await response.json();
// Format transactions
const transactions = await Promise.all((data._embedded.records || []).map(async tx => {
// Get operations for this transaction to determine type and details
const opsUrl = `${HORIZON_URL}/transactions/${tx.hash}/operations`;
let operations = [];
try {
const opsResponse = await fetch(opsUrl);
if (opsResponse.ok) {
const opsData = await opsResponse.json();
operations = opsData._embedded.records || [];
}
} catch (error) {
console.warn('Failed to fetch operations for transaction:', tx.hash, error);
}
// Find payment operations
const paymentOp = operations.find(op =>
op.type === 'payment' || op.type === 'create_account'
);
let type = 'other';
let amount = 0;
let amountXlm = 0;
let receiver = null;
let sender = tx.source_account;
if (paymentOp) {
if (paymentOp.type === 'payment') {
type = paymentOp.from === address ? 'sent' : 'received';
amount = parseFloat(paymentOp.amount || 0);
amountXlm = amount;
receiver = paymentOp.to;
sender = paymentOp.from;
} else if (paymentOp.type === 'create_account') {
type = paymentOp.funder === address ? 'sent' : 'received';
amount = parseFloat(paymentOp.starting_balance || 0);
amountXlm = amount;
receiver = paymentOp.account;
sender = paymentOp.funder;
}
}
// Parse timestamp
const timestamp = new Date(tx.created_at).getTime() / 1000;
return {
id: tx.id,
hash: tx.hash,
ledger: tx.ledger,
createdAt: tx.created_at,
sourceAccount: tx.source_account,
fee: parseInt(tx.fee_charged || tx.max_fee),
feeXlm: parseInt(tx.fee_charged || tx.max_fee) / 10000000,
operationCount: tx.operation_count,
successful: tx.successful,
// Payment details
type: type,
sender: sender,
receiver: receiver,
amount: amount,
amountXlm: amountXlm,
memo: tx.memo || null,
memoType: tx.memo_type || null,
// Compatibility fields
roundTime: timestamp,
confirmedRound: tx.ledger
};
}));
return {
transactions,
nextToken: data._embedded.records.length > 0
? data._embedded.records[data._embedded.records.length - 1].paging_token
: null,
hasMore: data._embedded.records.length === limit,
cursor: data._embedded.records.length > 0
? data._embedded.records[data._embedded.records.length - 1].paging_token
: null
};
} catch (error) {
throw error;
}
};
// Get transaction parameters (needed for sending)
stellarAPI.getTransactionParams = async function(sourceAddress) {
try {
// Get latest ledger info for fee stats
const response = await fetch(`${HORIZON_URL}/fee_stats`);
const feeStats = await response.json();
// Base fee in stroops (0.00001 XLM = 100 stroops)
const baseFee = feeStats.last_ledger_base_fee || '100';
return {
fee: parseInt(baseFee),
baseFee: baseFee,
networkPassphrase: StellarSdk ? StellarSdk.Networks.PUBLIC : 'Public Global Stellar Network ; September 2015',
genesisId: 'stellar-mainnet',
genesisHash: 'stellar-mainnet'
};
} catch (error) {
// Fallback to default fee
return {
fee: 100,
baseFee: '100',
networkPassphrase: StellarSdk ? StellarSdk.Networks.PUBLIC : 'Public Global Stellar Network ; September 2015',
genesisId: 'stellar-mainnet',
genesisHash: 'stellar-mainnet'
};
}
};
// Build and sign transaction using Stellar SDK
stellarAPI.buildAndSignTransaction = async function(params) {
const { sourceAddress, destinationAddress, amount, secretKey, memo } = params;
if (!StellarSdk || !server) {
throw new Error('Stellar SDK not initialized. Please refresh the page.');
}
try {
// Load source account
const sourceAccount = await server.loadAccount(sourceAddress);
// Check if destination account exists
let destinationExists = true;
try {
await server.loadAccount(destinationAddress);
} catch (error) {
if (error.response && error.response.status === 404) {
destinationExists = false;
} else {
throw error;
}
}
// Get fee stats
const feeStats = await server.feeStats();
// fee_charged.mode is typically 100 stroops (0.00001 XLM)
const fee = feeStats.fee_charged?.mode || feeStats.last_ledger_base_fee || '100';
// Build transaction
let transaction = new StellarSdk.TransactionBuilder(sourceAccount, {
fee: fee,
networkPassphrase: StellarSdk.Networks.PUBLIC
});
// Add operation based on whether destination exists
if (destinationExists) {
// Payment operation
transaction = transaction.addOperation(
StellarSdk.Operation.payment({
destination: destinationAddress,
asset: StellarSdk.Asset.native(),
amount: amount.toString()
})
);
} else {
// Create account operation (requires minimum 1 XLM)
if (parseFloat(amount) < 1) {
throw new Error('Creating a new account requires a minimum of 1 XLM');
}
transaction = transaction.addOperation(
StellarSdk.Operation.createAccount({
destination: destinationAddress,
startingBalance: amount.toString()
})
);
}
// Add memo if provided
if (memo) {
transaction = transaction.addMemo(StellarSdk.Memo.text(memo));
}
// Set timeout and build
transaction = transaction.setTimeout(30).build();
// Sign transaction
const keypair = StellarSdk.Keypair.fromSecret(secretKey);
transaction.sign(keypair);
return {
transaction: transaction,
xdr: transaction.toEnvelope().toXDR('base64'),
hash: transaction.hash().toString('hex'),
destinationExists: destinationExists,
fee: parseInt(fee)
};
} catch (error) {
console.error('Error building transaction:', error);
throw error;
}
};
// Submit signed transaction
stellarAPI.submitTransaction = async function(transactionXDR) {
if (!StellarSdk || !server) {
throw new Error('Stellar SDK not initialized. Please refresh the page.');
}
try {
// Parse the XDR back to a transaction using TransactionBuilder
const transaction = StellarSdk.TransactionBuilder.fromXDR(transactionXDR, StellarSdk.Networks.PUBLIC);
console.log('Submitting transaction to Stellar network...');
// Submit to network
const result = await server.submitTransaction(transaction);
console.log('✅ Transaction submitted successfully!');
console.log('Transaction Details:', {
hash: result.hash,
ledger: result.ledger,
successful: result.successful,
envelope_xdr: result.envelope_xdr,
result_xdr: result.result_xdr
});
return {
hash: result.hash,
ledger: result.ledger,
successful: result.successful,
txId: result.hash
};
} catch (error) {
console.error('❌ Error submitting transaction:', error);
// Parse Stellar error
if (error.response && error.response.data) {
const errorData = error.response.data;
let errorMsg = errorData.title || 'Transaction failed';
if (errorData.extras && errorData.extras.result_codes) {
const codes = errorData.extras.result_codes;
errorMsg += ': ' + (codes.transaction || codes.operations?.join(', ') || 'Unknown error');
}
throw new Error(errorMsg);
}
throw error;
}
};
// Get single transaction by hash
stellarAPI.getTransaction = async function(txHash) {
try {
const response = await fetch(`${HORIZON_URL}/transactions/${txHash}`);
if (!response.ok) {
throw new Error(`Transaction not found: ${response.status}`);
}
const tx = await response.json();
// Get operations for this transaction
const opsUrl = `${HORIZON_URL}/transactions/${tx.hash}/operations`;
let operations = [];
try {
const opsResponse = await fetch(opsUrl);
if (opsResponse.ok) {
const opsData = await opsResponse.json();
operations = opsData._embedded.records || [];
}
} catch (error) {
console.warn('Failed to fetch operations for transaction:', tx.hash, error);
}
// Find payment operations
const paymentOp = operations.find(op =>
op.type === 'payment' || op.type === 'create_account'
);
let type = 'other';
let amount = 0;
let amountXlm = 0;
let receiver = null;
let sender = tx.source_account;
if (paymentOp) {
if (paymentOp.type === 'payment') {
type = 'payment';
amount = parseFloat(paymentOp.amount || 0);
amountXlm = amount;
receiver = paymentOp.to;
sender = paymentOp.from;
} else if (paymentOp.type === 'create_account') {
type = 'create_account';
amount = parseFloat(paymentOp.starting_balance || 0);
amountXlm = amount;
receiver = paymentOp.account;
sender = paymentOp.funder;
}
}
// Parse timestamp
const timestamp = new Date(tx.created_at).getTime() / 1000;
return {
id: tx.id,
hash: tx.hash,
ledger: tx.ledger,
createdAt: tx.created_at,
sourceAccount: tx.source_account,
fee: parseInt(tx.fee_charged || tx.max_fee),
feeXlm: parseInt(tx.fee_charged || tx.max_fee) / 10000000,
operationCount: tx.operation_count,
successful: tx.successful,
// Payment details
type: type,
sender: sender,
receiver: receiver,
amount: amount,
amountXlm: amountXlm,
memo: tx.memo || null,
memoType: tx.memo_type || null,
operations: operations,
// Compatibility fields
roundTime: timestamp,
confirmedRound: tx.ledger
};
} catch (error) {
throw error;
}
};
// Format XLM amount for display
stellarAPI.formatXLM = function(amount) {
return parseFloat(amount).toFixed(7);
};
// Parse XLM to stroops (1 XLM = 10,000,000 stroops)
stellarAPI.parseXLM = function(xlm) {
return Math.floor(parseFloat(xlm) * 10000000);
};
// Validate Stellar address
stellarAPI.isValidAddress = function(address) {
// Stellar addresses start with 'G' and are 56 characters long
if (!address || typeof address !== 'string') return false;
if (address.length !== 56) return false;
if (!address.startsWith('G')) return false;
// Check if it's valid Base32
const BASE32_REGEX = /^[A-Z2-7]+$/;
return BASE32_REGEX.test(address);
};
// Validate Stellar secret key
stellarAPI.isValidSecret = function(secret) {
// Stellar secret keys start with 'S' and are 56 characters long
if (!secret || typeof secret !== 'string') return false;
if (secret.length !== 56) return false;
if (!secret.startsWith('S')) return false;
// Check if it's valid Base32
const BASE32_REGEX = /^[A-Z2-7]+$/;
return BASE32_REGEX.test(secret);
};
// Check initialization status
stellarAPI.isInitialized = function() {
return StellarSdk !== null && server !== null;
};
GLOBAL.stellarAPI = stellarAPI;
GLOBAL.xlmAPI = stellarAPI; // Alias for compatibility
// Auto-initialize when SDK is available with retry logic
if (typeof window !== 'undefined') {
let initAttempts = 0;
const maxAttempts = 5;
function tryInit() {
initAttempts++;
const success = stellarAPI.init();
if (success) {
} else if (initAttempts < maxAttempts) {
const delay = initAttempts * 200;
setTimeout(tryInit, delay);
} else {
console.error('❌ Failed to initialize Stellar SDK after', maxAttempts, 'attempts');
}
}
window.addEventListener('load', function() {
setTimeout(tryInit, 100);
});
}
})(typeof window !== 'undefined' ? window : global);

View File

@ -0,0 +1,303 @@
(function (EXPORTS) {
"use strict";
const stellarCrypto = EXPORTS;
// Helper functions
function hexToBytes(hex) {
const bytes = [];
for (let i = 0; i < hex.length; i += 2) {
bytes.push(parseInt(hex.substr(i, 2), 16));
}
return bytes;
}
function bytesToHex(bytes) {
return Array.from(bytes)
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
// Generate a new random key
function generateNewID() {
var key = new Bitcoin.ECKey(false);
key.setCompressed(true);
return {
floID: key.getBitcoinAddress(),
pubKey: key.getPubKeyHex(),
privKey: key.getBitcoinWalletImportFormat(),
};
}
// Calculate CRC16-XModem checksum (shared function)
function crc16XModem(data) {
let crc = 0x0000;
for (let i = 0; i < data.length; i++) {
crc ^= data[i] << 8;
for (let j = 0; j < 8; j++) {
if (crc & 0x8000) {
crc = (crc << 1) ^ 0x1021;
} else {
crc = crc << 1;
}
}
}
return crc & 0xFFFF;
}
// --- Multi-chain Generator (BTC, FLO, XLM) ---
stellarCrypto.generateMultiChain = async function (inputWif) {
const versions = {
BTC: { pub: 0x00, priv: 0x80 },
FLO: { pub: 0x23, priv: 0xa3 },
};
const origBitjsPub = bitjs.pub;
const origBitjsPriv = bitjs.priv;
const origBitjsCompressed = bitjs.compressed;
const origCoinJsCompressed = coinjs.compressed;
bitjs.compressed = true;
coinjs.compressed = true;
let privKeyHex;
let compressed = true;
if (typeof inputWif === "string" && inputWif.trim().length > 0) {
const trimmedInput = inputWif.trim();
const hexOnly = /^[0-9a-fA-F]+$/.test(trimmedInput);
// Check if it's a Stellar secret key (starts with 'S' and is 56 chars)
if (trimmedInput.startsWith('S') && trimmedInput.length === 56) {
try {
// Decode Stellar secret key (Base32)
const BASE32_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
const decoded = [];
let bits = 0;
let value = 0;
for (let i = 0; i < trimmedInput.length; i++) {
const char = trimmedInput[i];
const charValue = BASE32_ALPHABET.indexOf(char);
if (charValue === -1) throw new Error('Invalid Base32 character');
value = (value << 5) | charValue;
bits += 5;
while (bits >= 8) {
decoded.push((value >>> (bits - 8)) & 0xFF);
bits -= 8;
}
}
const decodedBytes = new Uint8Array(decoded);
// Validate checksum
if (decodedBytes.length < 35) {
throw new Error('Invalid Stellar secret key: too short');
}
// Extract components: [version(1)] + [seed(32)] + [checksum(2)]
const payload = decodedBytes.slice(0, 33); // version + seed
const providedChecksum = (decodedBytes[34] << 8) | decodedBytes[33]; // little-endian
// Calculate expected checksum
const expectedChecksum = crc16XModem(payload);
// Verify checksum matches
if (providedChecksum !== expectedChecksum) {
throw new Error(`Invalid Stellar secret key: checksum mismatch (expected ${expectedChecksum.toString(16)}, got ${providedChecksum.toString(16)})`);
}
// Verify version byte
if (decodedBytes[0] !== 0x90) {
throw new Error(`Invalid Stellar secret key: wrong version byte (expected 0x90, got 0x${decodedBytes[0].toString(16)})`);
}
// Extract seed (skip version byte, take 32 bytes)
const seed = decodedBytes.slice(1, 33);
privKeyHex = bytesToHex(seed);
} catch (e) {
console.error("Invalid Stellar secret key:", e.message);
throw new Error(`Failed to recover Stellar secret key: ${e.message}`);
}
} else if (hexOnly && (trimmedInput.length === 64 || trimmedInput.length === 128)) {
privKeyHex =
trimmedInput.length === 128 ? trimmedInput.substring(0, 64) : trimmedInput;
} else {
try {
const decode = Bitcoin.Base58.decode(trimmedInput);
// Validate WIF checksum
if (decode.length < 37) {
throw new Error('Invalid WIF key: too short');
}
// WIF format: [version(1)] + [private_key(32)] + [compression_flag(0-1)] + [checksum(4)]
const payload = decode.slice(0, decode.length - 4);
const providedChecksum = decode.slice(decode.length - 4);
// Calculate expected checksum using double SHA256
const hash1 = Crypto.SHA256(payload, { asBytes: true });
const hash2 = Crypto.SHA256(hash1, { asBytes: true });
const expectedChecksum = hash2.slice(0, 4);
// Verify checksum matches
let checksumMatch = true;
for (let i = 0; i < 4; i++) {
if (providedChecksum[i] !== expectedChecksum[i]) {
checksumMatch = false;
break;
}
}
if (!checksumMatch) {
const providedHex = providedChecksum.map(b => b.toString(16).padStart(2, '0')).join('');
const expectedHex = expectedChecksum.map(b => b.toString(16).padStart(2, '0')).join('');
throw new Error(`Invalid WIF key: checksum mismatch (expected ${expectedHex}, got ${providedHex})`);
}
const keyWithVersion = decode.slice(0, decode.length - 4);
let key = keyWithVersion.slice(1);
if (key.length >= 33 && key[key.length - 1] === 0x01) {
key = key.slice(0, key.length - 1);
compressed = true;
}
privKeyHex = bytesToHex(key);
} catch (e) {
console.error("Invalid WIF key:", e.message);
throw new Error(`Failed to recover from WIF key: ${e.message}`);
}
}
} else {
// Generate new key if no input
const newKey = generateNewID();
const decode = Bitcoin.Base58.decode(newKey.privKey);
const keyWithVersion = decode.slice(0, decode.length - 4);
let key = keyWithVersion.slice(1);
if (key.length >= 33 && key[key.length - 1] === 0x01)
key = key.slice(0, key.length - 1);
privKeyHex = bytesToHex(key);
}
// --- Derive addresses for each chain ---
const result = { BTC: {}, FLO: {}, XLM: {} };
// BTC
bitjs.pub = versions.BTC.pub;
bitjs.priv = versions.BTC.priv;
const pubKeyBTC = bitjs.newPubkey(privKeyHex);
result.BTC.address = coinjs.bech32Address(pubKeyBTC).address;
result.BTC.privateKey = bitjs.privkey2wif(privKeyHex);
// FLO
bitjs.pub = versions.FLO.pub;
bitjs.priv = versions.FLO.priv;
const pubKeyFLO = bitjs.newPubkey(privKeyHex);
result.FLO.address = bitjs.pubkey2address(pubKeyFLO);
result.FLO.privateKey = bitjs.privkey2wif(privKeyHex);
// XLM (Stellar)
try {
const privBytes = hexToBytes(privKeyHex.substring(0, 64));
const seed = new Uint8Array(privBytes.slice(0, 32));
// Generate Ed25519 keypair from seed
const keyPair = nacl.sign.keyPair.fromSeed(seed);
const pubKey = keyPair.publicKey;
// Stellar address encoding: version byte (0x30 for public key 'G') + public key + CRC16-XModem checksum
const versionByte = 0x30; // Results in 'G' prefix for public keys
const payload = new Uint8Array([versionByte, ...pubKey]);
const checksum = crc16XModem(payload);
// Checksum is stored in little-endian format
const checksumBytes = new Uint8Array([checksum & 0xFF, (checksum >> 8) & 0xFF]);
const addressBytes = new Uint8Array([...payload, ...checksumBytes]);
// Base32 encode the address (RFC 4648)
const BASE32_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
let bits = 0;
let value = 0;
let output = '';
for (let i = 0; i < addressBytes.length; i++) {
value = (value << 8) | addressBytes[i];
bits += 8;
while (bits >= 5) {
output += BASE32_ALPHABET[(value >>> (bits - 5)) & 31];
bits -= 5;
}
}
if (bits > 0) {
output += BASE32_ALPHABET[(value << (5 - bits)) & 31];
}
const xlmAddress = output;
// Stellar secret key format: version byte (0x90 for secret key 'S') + seed + CRC16
const secretVersionByte = 0x90; // Results in 'S' prefix for secret keys
const secretPayload = new Uint8Array([secretVersionByte, ...seed]);
const secretChecksum = crc16XModem(secretPayload);
const secretChecksumBytes = new Uint8Array([secretChecksum & 0xFF, (secretChecksum >> 8) & 0xFF]);
const secretKeyBytes = new Uint8Array([...secretPayload, ...secretChecksumBytes]);
// Base32 encode the secret key
bits = 0;
value = 0;
let secretOutput = '';
for (let i = 0; i < secretKeyBytes.length; i++) {
value = (value << 8) | secretKeyBytes[i];
bits += 8;
while (bits >= 5) {
secretOutput += BASE32_ALPHABET[(value >>> (bits - 5)) & 31];
bits -= 5;
}
}
if (bits > 0) {
secretOutput += BASE32_ALPHABET[(value << (5 - bits)) & 31];
}
const xlmPrivateKey = secretOutput;
result.XLM.address = xlmAddress;
result.XLM.privateKey = xlmPrivateKey;
} catch (error) {
console.error("Error generating XLM address:", error);
result.XLM.address = "Error generating address";
result.XLM.privateKey = privKeyHex;
}
bitjs.pub = origBitjsPub;
bitjs.priv = origBitjsPriv;
bitjs.compressed = origBitjsCompressed;
coinjs.compressed = origCoinJsCompressed;
return result;
};
// Sign Stellar Transaction
stellarCrypto.signXlm = async function (txBytes, xlmPrivateKey) {
const privKeyOnly = xlmPrivateKey.substring(0, 64);
const privBytes = hexToBytes(privKeyOnly);
const seed = new Uint8Array(privBytes.slice(0, 32));
const keypair = nacl.sign.keyPair.fromSeed(seed);
let txData;
if (typeof txBytes === 'string') {
txData = new Uint8Array(atob(txBytes).split('').map(c => c.charCodeAt(0)));
} else {
txData = new Uint8Array(txBytes);
}
const signature = nacl.sign.detached(txData, keypair.secretKey);
return signature;
};
})("object" === typeof module ? module.exports : (window.stellarCrypto = {}));

View File

@ -0,0 +1,120 @@
class SearchedAddressDB {
constructor() {
this.dbName = "StellarWalletDB";
this.version = 1;
this.storeName = "searchedAddresses";
this.db = null;
}
async init() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, this.version);
request.onerror = () => reject(request.error);
request.onsuccess = () => {
this.db = request.result;
resolve();
};
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains(this.storeName)) {
const store = db.createObjectStore(this.storeName, {
keyPath: "id",
autoIncrement: true
});
store.createIndex("timestamp", "timestamp", { unique: false });
store.createIndex("xlmAddress", "xlmAddress", { unique: false });
}
};
});
}
async saveSearchedAddress(
xlmAddress,
balance,
timestamp = Date.now(),
sourceInfo = null
) {
if (!this.db) await this.init();
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], "readwrite");
const store = transaction.objectStore(this.storeName);
const index = store.index("xlmAddress");
// Check if address already exists
const getRequest = index.getAll(xlmAddress);
getRequest.onsuccess = () => {
const existingRecords = getRequest.result;
if (existingRecords.length > 0) {
// Address exists, update the existing record
const existingRecord = existingRecords[0];
const updatedData = {
...existingRecord,
balance,
timestamp,
formattedBalance: `${balance.toFixed(7)} XLM`,
};
const putRequest = store.put(updatedData);
putRequest.onsuccess = () => resolve();
putRequest.onerror = () => reject(putRequest.error);
} else {
// Address doesn't exist, create new record
const data = {
xlmAddress,
btcAddress: sourceInfo?.btcAddress || null,
floAddress: sourceInfo?.floAddress || null,
balance,
timestamp,
formattedBalance: `${balance.toFixed(7)} XLM`,
isFromPrivateKey: !!(sourceInfo?.btcAddress || sourceInfo?.floAddress),
};
const addRequest = store.add(data);
addRequest.onsuccess = () => resolve();
addRequest.onerror = () => reject(addRequest.error);
}
};
getRequest.onerror = () => reject(getRequest.error);
});
}
async getSearchedAddresses() {
if (!this.db) await this.init();
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], "readonly");
const store = transaction.objectStore(this.storeName);
const index = store.index("timestamp");
const request = index.getAll();
request.onsuccess = () => {
const results = request.result.sort(
(a, b) => b.timestamp - a.timestamp
);
resolve(results.slice(0, 10));
};
request.onerror = () => reject(request.error);
});
}
async deleteSearchedAddress(id) {
if (!this.db) await this.init();
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], "readwrite");
const store = transaction.objectStore(this.storeName);
const request = store.delete(id);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}
async clearAllSearchedAddresses() {
if (!this.db) await this.init();
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], "readwrite");
const store = transaction.objectStore(this.storeName);
const request = store.clear();
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}
}

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 236.36 200">
<path fill="#000000" d="M203,26.16l-28.46,14.5-137.43,70a82.49,82.49,0,0,1-.7-10.69A81.87,81.87,0,0,1,158.2,28.6l16.29-8.3,2.43-1.24A100,100,0,0,0,18.18,100q0,3.82.29,7.61a18.19,18.19,0,0,1-9.88,17.58L0,129.57V150l25.29-12.89,0,0,8.19-4.18,8.07-4.11v0L186.43,55l16.28-8.29,33.65-17.15V9.14Z"/>
<path fill="#000000" d="M236.36,50,49.78,145,33.5,153.31,0,170.38v20.41l33.27-16.95,28.46-14.5L199.3,89.24A83.45,83.45,0,0,1,200,100,81.87,81.87,0,0,1,78.09,171.36l-1,.53-17.66,9A100,100,0,0,0,218.18,100c0-2.57-.1-5.14-.29-7.68a18.2,18.2,0,0,1,9.87-17.58l8.6-4.38Z"/>
</svg>

After

Width:  |  Height:  |  Size: 639 B

3381
stellarwallet/style.css Normal file

File diff suppressed because it is too large Load Diff