UNPKG

@goequitize/rwa-token-sdk

Version:

SDK for creating and managing RWA token transactions with compliance features

275 lines (274 loc) 11.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RwaTokenBase = void 0; const ethers_1 = require("ethers"); /** * Abstract base class for RWA Token operations across different chains */ class RwaTokenBase { /** * Create a new instance of the RwaTokenBase * @param provider The JSON-RPC provider instance * @param chainId The chain ID of the connected network */ constructor(provider, chainId) { this.balanceCache = new Map(); this.decimalsCache = new Map(); this.symbolCache = new Map(); this.provider = provider; this.chainId = chainId; } /** * Get the decimals of a token * @param tokenAddress The address of the token contract * @returns The number of decimals of the token */ async getDecimals(tokenAddress) { // Check cache first if (this.decimalsCache.has(tokenAddress)) { return this.decimalsCache.get(tokenAddress); } // Native token case if (tokenAddress.toLowerCase() === '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'.toLowerCase()) { return 18; // Native tokens (ETH, BNB, etc.) typically have 18 decimals } try { const tokenContract = new ethers_1.ethers.Contract(tokenAddress, ['function decimals() view returns (uint8)'], this.provider); const decimals = await tokenContract.decimals(); // Cache the result this.decimalsCache.set(tokenAddress, decimals); return decimals; } catch (error) { throw new Error(`Failed to get token decimals: ${error instanceof Error ? error.message : String(error)}`); } } /** * Get the symbol of a token * @param tokenAddress The address of the token contract * @returns The symbol of the token */ async getSymbol(tokenAddress) { // Check cache first if (this.symbolCache.has(tokenAddress)) { return this.symbolCache.get(tokenAddress); } // Native token case if (tokenAddress.toLowerCase() === '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'.toLowerCase()) { // Return appropriate symbol based on chainId switch (this.chainId) { case 1: // Ethereum Mainnet return 'ETH'; case 56: // Binance Smart Chain return 'BNB'; case 137: // Polygon return 'MATIC'; default: return 'NATIVE'; } } try { const tokenContract = new ethers_1.ethers.Contract(tokenAddress, ['function symbol() view returns (string)'], this.provider); const symbol = await tokenContract.symbol(); // Cache the result this.symbolCache.set(tokenAddress, symbol); return symbol; } catch (error) { throw new Error(`Failed to get token symbol: ${error instanceof Error ? error.message : String(error)}`); } } /** * Get the token balance of an address * @param tokenAddress The address of the token contract * @param walletAddress The address of the wallet to check the balance of * @returns The formatted token balance (e.g., 123.45) */ async getTokenBalance(tokenAddress, walletAddress) { // Generate a cache key const cacheKey = `${tokenAddress}_${walletAddress}`; // Native token case if (tokenAddress.toLowerCase() === '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'.toLowerCase()) { try { const balance = await this.provider.getBalance(walletAddress); return ethers_1.ethers.formatEther(balance); } catch (error) { throw new Error(`Failed to get native token balance: ${error instanceof Error ? error.message : String(error)}`); } } try { const tokenContract = new ethers_1.ethers.Contract(tokenAddress, [ 'function balanceOf(address account) view returns (uint256)', 'function decimals() view returns (uint8)' ], this.provider); const balanceWei = await tokenContract.balanceOf(walletAddress); const decimals = await this.getDecimals(tokenAddress); // Format the balance using the token's decimals const formattedBalance = ethers_1.ethers.formatUnits(balanceWei, decimals); return formattedBalance; } catch (error) { throw new Error(`Failed to get token balance: ${error instanceof Error ? error.message : String(error)}`); } } /** * Get information about current gas prices and estimated costs * @param txnType The type of transaction (used for gas estimation) * @returns Gas price information */ async getGasInfo(txnType) { try { const feeData = await this.provider.getFeeData(); // Determine multiplier for recommended gas based on transaction type let recommendedMultiplier = 1.2; // Default 20% markup if (txnType) { // Adjust multiplier based on transaction type switch (txnType.toLowerCase()) { case 'mint': case 'burn': recommendedMultiplier = 1.3; // 30% markup for more complex operations break; case 'transfer': recommendedMultiplier = 1.1; // 10% markup for simple transfers break; default: recommendedMultiplier = 1.2; // Default 20% markup } } // Use integer math for multiplier const multiplierInt = BigInt(Math.round(recommendedMultiplier * 100)); // Calculate recommended values with markup using integer math const recommendedGasPrice = feeData.gasPrice ? ethers_1.ethers.formatUnits((feeData.gasPrice * multiplierInt) / 100n, 'gwei') : '0'; const recommendedMaxFeePerGas = feeData.maxFeePerGas ? ethers_1.ethers.formatUnits((feeData.maxFeePerGas * multiplierInt) / 100n, 'gwei') : '0'; const recommendedMaxPriorityFeePerGas = feeData.maxPriorityFeePerGas ? ethers_1.ethers.formatUnits((feeData.maxPriorityFeePerGas * multiplierInt) / 100n, 'gwei') : '0'; return { market: { gasPrice: feeData.gasPrice ? ethers_1.ethers.formatUnits(feeData.gasPrice, 'gwei') : '0', maxFeePerGas: feeData.maxFeePerGas ? ethers_1.ethers.formatUnits(feeData.maxFeePerGas, 'gwei') : '0', maxPriorityFeePerGas: feeData.maxPriorityFeePerGas ? ethers_1.ethers.formatUnits(feeData.maxPriorityFeePerGas, 'gwei') : '0', }, recommended: { gasPrice: recommendedGasPrice, maxFeePerGas: recommendedMaxFeePerGas, maxPriorityFeePerGas: recommendedMaxPriorityFeePerGas, } }; } catch (error) { throw new Error(`Failed to get gas info: ${error instanceof Error ? error.message : String(error)}`); } } /** * Sign a transaction using a private key * @param rawTx The unsigned transaction * @param privateKey The private key to sign with * @returns The signed transaction */ async signTxn(rawTx, privateKey) { try { // Create a wallet from the private key const wallet = new ethers_1.ethers.Wallet(privateKey, this.provider); // Sign the transaction const signedTx = await wallet.signTransaction({ to: rawTx.to, data: rawTx.data, value: rawTx.value ? ethers_1.ethers.parseEther(rawTx.value) : undefined, gasLimit: rawTx.gasLimit ? BigInt(rawTx.gasLimit) : undefined, maxFeePerGas: rawTx.maxFeePerGas ? ethers_1.ethers.parseUnits(rawTx.maxFeePerGas, 'gwei') : undefined, maxPriorityFeePerGas: rawTx.maxPriorityFeePerGas ? ethers_1.ethers.parseUnits(rawTx.maxPriorityFeePerGas, 'gwei') : undefined, chainId: this.chainId }); return signedTx; } catch (error) { throw new Error(`Failed to sign transaction: ${error instanceof Error ? error.message : String(error)}`); } } /** * Broadcast a signed transaction to the network * @param signedTxn The signed transaction hex string * @returns Transaction response information */ async broadcast(signedTxn) { try { // Send the raw transaction const tx = await this.provider.broadcastTransaction(signedTxn); // Construct an explorer URL based on the chain ID let explorerURL = ''; switch (this.chainId) { case 1: // Ethereum Mainnet explorerURL = `https://etherscan.io/tx/${tx.hash}`; break; case 5: // Goerli Testnet explorerURL = `https://goerli.etherscan.io/tx/${tx.hash}`; break; case 56: // Binance Smart Chain explorerURL = `https://bscscan.com/tx/${tx.hash}`; break; case 97: // BSC Testnet explorerURL = `https://testnet.bscscan.com/tx/${tx.hash}`; break; case 137: // Polygon explorerURL = `https://polygonscan.com/tx/${tx.hash}`; break; case 80001: // Mumbai (Polygon Testnet) explorerURL = `https://mumbai.polygonscan.com/tx/${tx.hash}`; break; default: explorerURL = ''; } return { hash: tx.hash, explorerURL }; } catch (error) { throw new Error(`Failed to broadcast transaction: ${error instanceof Error ? error.message : String(error)}`); } } /** * Get the logs and status of a transaction * @param txHash The transaction hash * @returns The transaction logs and status */ async getTxnLogs(txHash) { try { // Get the transaction receipt const receipt = await this.provider.getTransactionReceipt(txHash); if (!receipt) { // Transaction is still pending return { status: 'pending', logs: [] }; } // Determine status based on receipt const status = receipt.status === 1 ? 'success' : 'failed'; // Process the logs const processedLogs = receipt.logs.map(log => { // In a real implementation, we would decode the logs based on ABI // For now, return the raw logs return { address: log.address, topics: log.topics, data: log.data }; }); return { status, logs: processedLogs }; } catch (error) { throw new Error(`Failed to get transaction logs: ${error instanceof Error ? error.message : String(error)}`); } } } exports.RwaTokenBase = RwaTokenBase;