import Web3 from "web3"; import { abiERC20 } from "../abis/abiERC20"; import { BN, toUtf8 } from "ethereumjs-util"; import { universalChainCb } from "../util/chain.util"; export class ERC20Standard { private web3: Web3; constructor(web3: Web3) { this.web3 = web3; } /** * Get balance or count for current account on specific asset contract. * * @param address - Asset ERC20 contract address. * @param selectedAddress - Current account public address. * @returns Promise resolving to BN object containing balance for current account on specific asset contract. */ async getBalanceOf(address: string, selectedAddress: string): Promise { const contract = new this.web3.eth.Contract(abiERC20, address); return new Promise((resolve, reject) => { contract.methods .balanceOf(selectedAddress) .call({ from: selectedAddress }, (error: Error, result: BN) => { /* istanbul ignore if */ if (error) { reject(error); return; } console.log("getBalanceOf success " + result); resolve(result); }); }); } /** * Query for the decimals for a given ERC20 asset. * * @param address - ERC20 asset contract string. * @returns Promise resolving to the 'decimals'. */ async getTokenDecimals(address: string): Promise { const contract = new this.web3.eth.Contract(abiERC20, address); return new Promise((resolve, reject) => { contract.methods.decimals().call((error: Error, result: BN | string) => { /* istanbul ignore if */ if (error) { reject(error); return; } resolve(result.toString()); }); }); } /** * Query for symbol for a given ERC20 asset. * * @param address - ERC20 asset contract address. * @returns Promise resolving to the 'symbol'. */ async getTokenSymbol(address: string): Promise { const contract = new this.web3.eth.Contract(abiERC20, address); return new Promise((resolve, reject) => { contract.methods.symbol().call((error: Error, result: BN | string) => { /* istanbul ignore if */ if (error) { reject(error); return; } resolve(result.toString()); }); }); // Signature for calling `symbol()` // const payload = { to: address, data: '0x95d89b41' }; // return new Promise((resolve, reject) => { // this.web3.eth.call(payload, undefined, (error: Error, result: string) => { // /* istanbul ignore if */ // if (error) { // reject(error); // return; // } // // Parse as string // try { // const decoded = Web3.utils.hexToUtf8(result); // if (decoded) { // console.log('decoded') // resolve(decoded); // return; // } // } catch { // // Ignore error // } // // Parse as bytes // try { // const utf8 = Web3.utils.toUtf8(result); // console.log('utf8') // resolve(utf8); // return; // } catch { // // Ignore error // } // reject(new Error('Failed to parse token symbol')); // }); // }); } /** * Query if a contract implements an interface. * * @param address - Asset contract address. * @param userAddress - The public address for the currently active user's account. * @returns Promise resolving an object containing the standard, decimals, symbol and balance of the given contract/userAddress pair. */ async getDetails( address: string, userAddress?: string ): Promise<{ standard: string; symbol: string | undefined; decimals: string | undefined; balance: BN | undefined; }> { const [decimals, symbol] = await Promise.all([ this.getTokenDecimals(address), this.getTokenSymbol(address), ]); let balance; if (userAddress) { balance = await this.getBalanceOf(address, userAddress); } return { decimals, symbol, balance, standard: "ERC20", }; } async transfer({ address, from, to, amount, gas, estimate, }: { address: string; from: string; to: string; amount: number | string; gas?: number; estimate: number; }) { const contract = new this.web3.eth.Contract(abiERC20, address); const amountBN = Web3.utils.toBN(Web3.utils.toWei(amount + "")); if (!gas) { gas = await contract.methods.transfer(to, "0").estimateGas(); } gas = (gas * 1.1) | 1; if (estimate) { return jc.wallet.generateGasShow(gas); } const logData = { gas, title: "transfer", details: [ { address, from, to, value: amountBN, id: "0", }, ], }; return universalChainCb( logData, contract.methods.transfer(to, amountBN).send({ from, gas, }) ); } }