@broxus/js-bridge-essentials
Version:
Bridge JavaScript Essentials library
174 lines (173 loc) • 6.31 kB
JavaScript
import { erc20Contract } from '../../models/evm-token/contracts';
const cache = new Map();
const ttl = 30 * 1000;
export class EvmTokenUtils {
static allowance(connection, params) {
return erc20Contract(connection, params.tokenAddress)
.methods.allowance(params.ownerAddress, params.spenderAddress)
.call();
}
static async approve(connection, params) {
const req = erc20Contract(connection, params.tokenAddress).methods.approve(params.spenderAddress, params.amount);
const gas = await req?.estimateGas({
from: params.from,
type: params.type,
});
return req?.send({
from: params.from,
gas: gas.toString(),
type: params.type,
});
}
/** @deprecated Use EvmTokenUtils.balance instead */
static getBalance(connection, address, ownerAddress) {
return EvmTokenUtils.balance(connection, address, ownerAddress);
}
/**
* Get EVM token balance by the given token and owner addresses
* @param {Web3} connection
* @param {string} address
* @param {string} ownerAddress
* @returns {Promise<string>}
*/
static balance(connection, address, ownerAddress) {
return erc20Contract(connection, address).methods.balanceOf(ownerAddress).call();
}
/**
*
* @param {Web3} connection
* @param {string} address
* @param {Forceable} options
* @returns {Promise<EvmTokenData | undefined>}
*/
static async getDetails(connection, address, options) {
const netId = await connection.eth.net.getId();
const cached = cache.get(`${netId}-${address.toLowerCase()}`);
const abi = [
{
name: 'decimals',
type: 'uint8',
},
{
name: 'name',
type: 'string',
},
{
name: 'symbol',
type: 'string',
},
{
name: 'totalSupply',
type: 'uint256',
},
];
const reducer = (acc, r, idx) => {
if (r.result == null) {
throw new Error(`Failed to get ${abi[idx].name} for token ${address}`);
}
if (typeof r.result === 'string') {
const decoded = connection.eth.abi.decodeParameter(abi[idx].type, r.result);
if (abi[idx].name === 'decimals') {
acc.decimals = parseInt(BigInt(decoded).toString(), 10);
return acc;
}
if (abi[idx].name === 'totalSupply') {
acc.totalSupply = BigInt(decoded).toString();
return acc;
}
acc[abi[idx].name] = decoded;
return acc;
}
throw new Error(`Failed to parse ${abi[idx].name} for token ${address}. Given result is not a string`);
};
const fallback = async () => {
const [decimals, name, symbol, totalSupply] = await Promise.all([
EvmTokenUtils.getDecimals(connection, address),
EvmTokenUtils.getName(connection, address),
EvmTokenUtils.getSymbol(connection, address),
EvmTokenUtils.totalSupply(connection, address),
]);
return { address, decimals, name, symbol, totalSupply };
};
if (cached && !options?.force) {
if (Date.now() - cached.timestamp < ttl && cached.data) {
const res = await cached.data;
return res.reduce(reducer, {});
}
}
try {
const contract = erc20Contract(connection, address);
const batch = new contract.BatchRequest();
batch.add({
method: 'eth_call',
params: [{
data: contract.methods.decimals().encodeABI(),
to: address,
}, 'latest'],
});
batch.add({
method: 'eth_call',
params: [{
data: contract.methods.name().encodeABI(),
to: address,
}, 'latest'],
});
batch.add({
method: 'eth_call',
params: [{
data: contract.methods.symbol().encodeABI(),
to: address,
}, 'latest'],
});
batch.add({
method: 'eth_call',
params: [{
data: contract.methods.totalSupply().encodeABI(),
to: address,
}, 'latest'],
});
const req = batch.execute({ timeout: 30_000 });
cache.set(`${netId}-${address.toLowerCase()}`, { data: req, timestamp: Date.now() });
const res = await req;
return res.reduce(reducer, {});
}
catch (e) {
return fallback();
}
}
/**
* Get EVM token decimals by the given token address
* @param {Web3} connection
* @param {string} address
* @returns {Promise<number>}
*/
static async getDecimals(connection, address) {
const result = await erc20Contract(connection, address).methods.decimals().call();
return parseInt(BigInt(result).toString(), 10);
}
/**
* Get EVM token symbol by the given token address
* @param {Web3} connection
* @param {string} address
*/
static getSymbol(connection, address) {
return erc20Contract(connection, address).methods.symbol().call();
}
/**
* Get EVM token name by the given token address
* @param {Web3} connection
* @param {string} address
*/
static getName(connection, address) {
return erc20Contract(connection, address).methods.name().call();
}
/**
* Get EVM token total supply by the given token address
* @param {Web3} connection
* @param {string} address
* @returns {Promise<string>}
*/
static totalSupply(connection, address) {
return erc20Contract(connection, address).methods.totalSupply().call();
}
}