UNPKG

p-sdk-wallet

Version:

A comprehensive wallet SDK for React Native (pwc), supporting multi-chain and multi-account features.

430 lines (429 loc) 17.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getProviderAuto = getProviderAuto; exports.getTokenBalance = getTokenBalance; exports.getTokenInfo = getTokenInfo; exports.getNativeBalance = getNativeBalance; exports.checkAllowance = checkAllowance; exports.buildTransaction = buildTransaction; exports.signTransactionWithVault = signTransactionWithVault; exports.approveTokenWithVault = approveTokenWithVault; exports.sendTransaction = sendTransaction; exports.trackTxStatus = trackTxStatus; exports.getNonce = getNonce; exports.signMessageWithVault = signMessageWithVault; exports.signMessage = signMessage; exports.verifyMessage = verifyMessage; exports.signTypedDataWithVault = signTypedDataWithVault; exports.signTypedData = signTypedData; exports.verifyTypedData = verifyTypedData; exports.signMessageWithNonce = signMessageWithNonce; exports.signMessageWithNoncePrivateKey = signMessageWithNoncePrivateKey; exports.verifyMessageWithNonce = verifyMessageWithNonce; const ethers_1 = require("ethers"); const chains_1 = require("../config/chains"); const ERC20_ABI = [ "function name() view returns (string)", "function symbol() view returns (string)", "function decimals() view returns (uint8)", "function totalSupply() view returns (uint256)", "function balanceOf(address) view returns (uint)", "function transfer(address to, uint amount) returns (bool)", "function approve(address spender, uint amount) returns (bool)", "function allowance(address owner, address spender) view returns (uint)" ]; // Provider manager: cache provider per chainId const providerCache = {}; /** * Gets or creates a cached provider for the specified chain. * @param chainId - The chain ID (e.g., '1' for Ethereum, '56' for BSC) * @returns Cached ethers provider for the chain */ function getProviderAuto(chainId) { if (!providerCache[chainId]) { providerCache[chainId] = (0, chains_1.getProvider)(chainId); } return providerCache[chainId]; } /** * Gets the token balance for a specific account. * @param tokenAddress - The ERC-20 token contract address * @param account - The account address to check balance for * @param chainId - The chain ID (e.g., '1' for Ethereum, '56' for BSC) * @returns Promise resolving to the token balance as bigint * @example * ```typescript * const balance = await getTokenBalance('0xToken...', '0xAccount...', '1'); * console.log('Balance:', ethers.formatUnits(balance, 18)); * ``` */ async function getTokenBalance(tokenAddress, account, chainId) { const provider = getProviderAuto(chainId); const contract = new ethers_1.Contract(tokenAddress, ERC20_ABI, provider); return contract.balanceOf(account); } /** * Gets comprehensive information about an ERC-20 token. * @param tokenAddress - The ERC-20 token contract address * @param chainId - The chain ID (e.g., '1' for Ethereum, '56' for BSC) * @returns Promise resolving to token information including name, symbol, decimals, total supply * @example * ```typescript * const info = await getTokenInfo('0xToken...', '1'); * console.log('Token:', info.name, '(', info.symbol, ')'); * ``` */ async function getTokenInfo(tokenAddress, chainId) { const provider = getProviderAuto(chainId); const contract = new ethers_1.Contract(tokenAddress, ERC20_ABI, provider); const [name, symbol, decimals, totalSupply] = await Promise.all([ contract.name(), contract.symbol(), contract.decimals(), contract.totalSupply() ]); return { name, symbol, decimals, totalSupply, address: tokenAddress }; } /** * Gets the native token balance (ETH, BNB, MATIC, etc.) for an account. * @param account - The account address to check balance for * @param chainId - The chain ID (e.g., '1' for Ethereum, '56' for BSC) * @returns Promise resolving to the native token balance as bigint * @example * ```typescript * const balance = await getNativeBalance('0xAccount...', '1'); * console.log('ETH Balance:', ethers.formatEther(balance)); * ``` */ async function getNativeBalance(account, chainId) { const provider = getProviderAuto(chainId); return provider.getBalance(account); } /** * Checks the allowance granted by an owner to a spender for a specific token. * @param tokenAddress - The ERC-20 token contract address * @param owner - The token owner's address * @param spender - The spender's address (e.g., DEX contract address) * @param chainId - The chain ID (e.g., '1' for Ethereum, '56' for BSC) * @returns Promise resolving to the allowance amount as bigint * @example * ```typescript * const allowance = await checkAllowance('0xToken...', '0xOwner...', '0xSpender...', '1'); * console.log('Allowance:', ethers.formatUnits(allowance, 18)); * ``` */ async function checkAllowance(tokenAddress, owner, spender, chainId) { const provider = getProviderAuto(chainId); const contract = new ethers_1.Contract(tokenAddress, ERC20_ABI, provider); return contract.allowance(owner, spender); } /** * Builds a generic transaction for any contract method. * @param contractAddress - The contract address to interact with * @param abi - The contract ABI array * @param method - The method name to call * @param args - Arguments to pass to the method (default: []) * @param value - Native token value to send with transaction (default: undefined) * @param chainId - The chain ID (e.g., '1' for Ethereum, '56' for BSC) * @returns Promise resolving to the unsigned transaction object * @example * ```typescript * const tx = await buildTransaction({ * contractAddress: '0xContract...', * abi: [...], * method: 'transfer', * args: ['0xTo...', '1000000000000000000'], * chainId: '1' * }); * ``` */ async function buildTransaction({ contractAddress, abi, method, args = [], value = undefined, chainId }) { const provider = getProviderAuto(chainId); const contract = new ethers_1.Contract(contractAddress, abi, provider); const data = contract.interface.encodeFunctionData(method, args); return { to: contractAddress, data, value }; } /** * Signs an unsigned transaction using Vault (RECOMMENDED). * @param unsignedTx - The unsigned transaction object * @param vault - The vault instance containing the account * @param fromAddress - The sender's address * @param chainId - The chain ID (e.g., '1' for Ethereum, '56' for BSC) * @returns Promise resolving to the signed transaction as hex string * @example * ```typescript * const signedTx = await signTransactionWithVault(unsignedTx, vault, '0xYourAddress', '1'); * ``` */ async function signTransactionWithVault(unsignedTx, vault, fromAddress, chainId) { const privateKey = await vault.getPrivateKeyFor(fromAddress); const provider = (0, chains_1.getProvider)(chainId); const wallet = new ethers_1.Wallet(privateKey, provider); return wallet.signTransaction(unsignedTx); } /** * Approves a spender to spend tokens on behalf of the owner using Vault (RECOMMENDED). * @param vault - The vault instance containing the owner's account * @param fromAddress - The owner's address * @param tokenAddress - The ERC-20 token contract address * @param spender - The spender's address (e.g., DEX contract address) * @param amount - The amount to approve (string or bigint) * @param chainId - The chain ID (e.g., '1' for Ethereum, '56' for BSC) * @returns Promise resolving to the transaction response * @example * ```typescript * const receipt = await approveTokenWithVault(vault, '0xOwner...', '0xToken...', '0xSpender...', '1000', '1'); * ``` */ async function approveTokenWithVault(vault, fromAddress, tokenAddress, spender, amount, chainId) { const privateKey = await vault.getPrivateKeyFor(fromAddress); const provider = (0, chains_1.getProvider)(chainId); const wallet = new ethers_1.Wallet(privateKey, provider); const contract = new ethers_1.Contract(tokenAddress, ERC20_ABI, wallet); const decimals = await contract.decimals(); const parsedAmount = typeof amount === 'string' ? ethers_1.ethers.parseUnits(amount, decimals) : amount; const tx = await contract.approve(spender, parsedAmount); if (typeof tx.wait === 'function') { return tx.wait(); } return tx; } /** * Broadcasts a signed transaction to the network. * @param signedTx - The signed transaction as hex string * @param chainId - The chain ID (e.g., '1' for Ethereum, '56' for BSC) * @returns Promise resolving to the transaction response * @example * ```typescript * const txResponse = await sendTransaction(signedTx, '1'); * console.log('Transaction hash:', txResponse.hash); * ``` */ async function sendTransaction(signedTx, chainId) { const provider = getProviderAuto(chainId); return provider.broadcastTransaction(signedTx); } /** * Tracks the status of a transaction with polling and callback notifications. * @param txHash - The transaction hash to track * @param chainId - The chain ID (e.g., '1' for Ethereum, '56' for BSC) * @param callback - Function called with status updates ('pending', 'confirmed', 'failed') * @param pollInterval - Polling interval in milliseconds (default: 3000) * @example * ```typescript * trackTxStatus('0xTxHash...', '1', (status, receipt) => { * if (status === 'confirmed') { * console.log('Transaction confirmed!'); * } * }); * ``` */ async function trackTxStatus(txHash, chainId, callback, pollInterval = 3000) { const provider = getProviderAuto(chainId); let done = false; while (!done) { const receipt = await provider.getTransactionReceipt(txHash); if (receipt) { if (receipt.status === 1) { callback('confirmed', receipt); } else { callback('failed', receipt); } done = true; } else { callback('pending'); await new Promise(res => setTimeout(res, pollInterval)); } } } /** * Gets the transaction count (nonce) for an address. * This is useful for message signing with nonce and transaction building. * @param address - The address to get nonce for * @param chainId - The chain ID (e.g., '1' for Ethereum, '56' for BSC) * @returns Promise resolving to the nonce as number * @example * ```typescript * const nonce = await getNonce('0xYourAddress', '1'); * console.log('Current nonce:', nonce); * ``` */ async function getNonce(address, chainId) { const provider = getProviderAuto(chainId); return provider.getTransactionCount(address); } /** * Signs a message using Vault (RECOMMENDED). * This is equivalent to MetaMask's personal_sign method. * @param message - The message to sign (string or hex string) * @param vault - The vault instance containing the account * @param fromAddress - The signer's address * @returns Promise resolving to the signature as hex string * @example * ```typescript * const signature = await signMessageWithVault('Hello World', vault, '0xYourAddress'); * console.log('Signature:', signature); * ``` */ async function signMessageWithVault(message, vault, fromAddress) { const privateKey = await vault.getPrivateKeyFor(fromAddress); const wallet = new ethers_1.Wallet(privateKey); return wallet.signMessage(message); } /** * Signs a message using private key (DEPRECATED). * ⚠️ **DEPRECATED**: Use signMessageWithVault instead for better security. * @param message - The message to sign (string or hex string) * @param privateKey - The private key to sign with (without 0x prefix) * @returns Promise resolving to the signature as hex string * @example * ```typescript * const signature = await signMessage('Hello World', 'your-private-key'); * console.log('Signature:', signature); * ``` */ async function signMessage(message, privateKey) { const wallet = new ethers_1.Wallet(privateKey); return wallet.signMessage(message); } /** * Verifies a message signature and returns the signer's address. * @param message - The original message that was signed * @param signature - The signature to verify * @returns Promise resolving to the signer's address * @example * ```typescript * const signerAddress = await verifyMessage('Hello World', '0xSignature...'); * console.log('Message was signed by:', signerAddress); * ``` */ async function verifyMessage(message, signature) { return ethers_1.ethers.verifyMessage(message, signature); } /** * Signs a typed data message using Vault (RECOMMENDED). * This is equivalent to MetaMask's eth_signTypedData method. * @param typedData - The typed data object to sign * @param vault - The vault instance containing the account * @param fromAddress - The signer's address * @returns Promise resolving to the signature as hex string * @example * ```typescript * const typedData = { * types: { * Person: [ * { name: 'name', type: 'string' }, * { name: 'wallet', type: 'address' } * ] * }, * primaryType: 'Person', * domain: { name: 'MyApp', version: '1' }, * message: { name: 'Alice', wallet: '0x123...' } * }; * const signature = await signTypedDataWithVault(typedData, vault, '0xYourAddress'); * ``` */ async function signTypedDataWithVault(typedData, vault, fromAddress) { const privateKey = await vault.getPrivateKeyFor(fromAddress); const wallet = new ethers_1.Wallet(privateKey); return wallet.signTypedData(typedData.domain, typedData.types, typedData.message); } /** * Signs a typed data message using private key (DEPRECATED). * ⚠️ **DEPRECATED**: Use signTypedDataWithVault instead for better security. * @param typedData - The typed data object to sign * @param privateKey - The private key to sign with (without 0x prefix) * @returns Promise resolving to the signature as hex string * @example * ```typescript * const typedData = { * types: { Person: [{ name: 'name', type: 'string' }] }, * primaryType: 'Person', * domain: { name: 'MyApp' }, * message: { name: 'Alice' } * }; * const signature = await signTypedData(typedData, 'your-private-key'); * ``` */ async function signTypedData(typedData, privateKey) { const wallet = new ethers_1.Wallet(privateKey); return wallet.signTypedData(typedData.domain, typedData.types, typedData.message); } /** * Verifies a typed data signature and returns the signer's address. * @param typedData - The original typed data that was signed * @param signature - The signature to verify * @returns Promise resolving to the signer's address * @example * ```typescript * const signerAddress = await verifyTypedData(typedData, '0xSignature...'); * console.log('Typed data was signed by:', signerAddress); * ``` */ async function verifyTypedData(typedData, signature) { return ethers_1.ethers.verifyTypedData(typedData.domain, typedData.types, typedData.message, signature); } /** * Signs a message with nonce using Vault (RECOMMENDED). * This includes nonce in the message to prevent replay attacks. * @param message - The message to sign (string or hex string) * @param vault - The vault instance containing the account * @param fromAddress - The signer's address * @param chainId - The chain ID (e.g., '1' for Ethereum, '56' for BSC) * @returns Promise resolving to an object containing signature and nonce * @example * ```typescript * const result = await signMessageWithNonce('Hello World', vault, '0xYourAddress', '1'); * console.log('Signature:', result.signature); * console.log('Nonce:', result.nonce); * ``` */ async function signMessageWithNonce(message, vault, fromAddress, chainId) { const nonce = await getNonce(fromAddress, chainId); const messageWithNonce = `${message}\nNonce: ${nonce}`; const signature = await signMessageWithVault(messageWithNonce, vault, fromAddress); return { signature, nonce }; } /** * Signs a message with nonce using private key (DEPRECATED). * ⚠️ **DEPRECATED**: Use signMessageWithNonceWithVault instead for better security. * @param message - The message to sign (string or hex string) * @param privateKey - The private key to sign with (without 0x prefix) * @param address - The signer's address (needed to get nonce) * @param chainId - The chain ID (e.g., '1' for Ethereum, '56' for BSC) * @returns Promise resolving to an object containing signature and nonce * @example * ```typescript * const result = await signMessageWithNoncePrivateKey('Hello World', 'your-private-key', '0xYourAddress', '1'); * console.log('Signature:', result.signature); * console.log('Nonce:', result.nonce); * ``` */ async function signMessageWithNoncePrivateKey(message, privateKey, address, chainId) { const nonce = await getNonce(address, chainId); const messageWithNonce = `${message}\nNonce: ${nonce}`; const signature = await signMessage(messageWithNonce, privateKey); return { signature, nonce }; } /** * Verifies a message signature with nonce and returns the signer's address. * @param message - The original message that was signed (without nonce) * @param signature - The signature to verify * @param nonce - The nonce that was used in signing * @returns Promise resolving to the signer's address * @example * ```typescript * const signerAddress = await verifyMessageWithNonce('Hello World', '0xSignature...', 5); * console.log('Message was signed by:', signerAddress); * ``` */ async function verifyMessageWithNonce(message, signature, nonce) { const messageWithNonce = `${message}\nNonce: ${nonce}`; return verifyMessage(messageWithNonce, signature); }