Merge pull request #1 from void-57/main

RanchiMall Stellar Wallet - Complete Feature Set
This commit is contained in:
Aniruddha 2025-12-17 05:40:26 +05:30 committed by GitHub
commit 386dfdc848
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 17718 additions and 0 deletions

1981
index.html Normal file

File diff suppressed because it is too large Load Diff

11473
lib.stellar.js Normal file

File diff suppressed because it is too large Load Diff

496
stellarBlockchainAPI.js Normal file
View File

@ -0,0 +1,496 @@
(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();
const fee = feeStats.max_fee.mode || (StellarSdk.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
const transaction = new StellarSdk.Transaction(transactionXDR, StellarSdk.Networks.PUBLIC);
// Submit to network
const result = await server.submitTransaction(transaction);
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);

263
stellarCrypto.js Normal file
View File

@ -0,0 +1,263 @@
(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(),
};
}
// --- 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);
// Extract seed (skip version byte 0x90, take 32 bytes, ignore checksum)
const seed = decodedBytes.slice(1, 33);
privKeyHex = bytesToHex(seed);
} catch (e) {
console.warn("Invalid Stellar secret key:", e);
// Fall through to generate new key
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);
}
} 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);
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.warn("Invalid WIF, generating new key:", e);
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);
}
}
} 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]);
// Calculate CRC16-XModem checksum
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;
}
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 = {}));

120
stellarSearchDB.js Normal file
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);
});
}
}

4
stellar_favicon.svg Normal file
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
style.css Normal file

File diff suppressed because it is too large Load Diff