UNPKG

rubic-sdk

Version:
287 lines 12.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EvmWeb3Public = void 0; const bignumber_js_1 = __importDefault(require("bignumber.js")); const errors_1 = require("../../../../../common/errors"); const p_timeout_1 = __importDefault(require("../../../../../common/utils/p-timeout")); const healthcheck_1 = require("../../../constants/healthcheck"); const erc_20_token_abi_1 = require("./constants/erc-20-token-abi"); const evm_multicall_abi_1 = require("./constants/evm-multicall-abi"); const tx_status_1 = require("../models/tx-status"); const web3_public_1 = require("../web3-public"); const evm_web3_pure_1 = require("../../../web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure"); const default_http_client_1 = require("../../../../http-client/default-http-client"); /** * Class containing methods for calling contracts in order to obtain information from the blockchain. * To send transaction or execute contract method use {@link Web3Private}. */ class EvmWeb3Public extends web3_public_1.Web3Public { constructor(web3, blockchainName, httpClient) { super(blockchainName); this.web3 = web3; this.httpClient = httpClient; this.tokenContractAbi = erc_20_token_abi_1.ERC20_TOKEN_ABI; } setProvider(provider) { this.web3.setProvider(provider); } async healthCheck(timeoutMs = 4000) { if (!(0, healthcheck_1.isBlockchainHealthcheckAvailable)(this.blockchainName)) { return true; } const healthcheckData = healthcheck_1.HEALTHCHECK[this.blockchainName]; const contract = new this.web3.eth.Contract(healthcheckData.contractAbi, healthcheckData.contractAddress); try { const result = await (0, p_timeout_1.default)(contract.methods[healthcheckData.method]().call(), timeoutMs); return result === healthcheckData.expected; } catch (e) { if (e instanceof errors_1.TimeoutError) { console.debug(`${this.blockchainName} node healthcheck timeout (${timeoutMs}ms) has occurred.`); } else { console.debug(`${this.blockchainName} node healthcheck fail: ${e}`); } return false; } } async getBalance(userAddress, tokenAddress) { let balance; if (tokenAddress && !evm_web3_pure_1.EvmWeb3Pure.isNativeAddress(tokenAddress)) { balance = await this.getTokenBalance(userAddress, tokenAddress); } else { balance = await this.web3.eth.getBalance(userAddress); } return new bignumber_js_1.default(balance); } async getTokenBalance(userAddress, tokenAddress) { const contract = new this.web3.eth.Contract(this.tokenContractAbi, tokenAddress); const balance = await contract.methods.balanceOf(userAddress).call(); return new bignumber_js_1.default(balance); } async getAllowance(tokenAddress, ownerAddress, spenderAddress) { const contract = new this.web3.eth.Contract(this.tokenContractAbi, tokenAddress); const allowance = await contract.methods.allowance(ownerAddress, spenderAddress).call(); return new bignumber_js_1.default(allowance); } async multicallContractsMethods(contractAbi, contractsData) { if (this.multicallAddress) { const calls = contractsData.map(({ contractAddress, methodsData }) => { const contract = new this.web3.eth.Contract(contractAbi, contractAddress); return methodsData.map(({ methodName, methodArguments }) => ({ callData: contract.methods[methodName](...methodArguments).encodeABI(), target: contractAddress })); }); const outputs = await this.multicall(calls.flat()); let outputIndex = 0; return contractsData.map(contractData => contractData.methodsData.map(methodData => { const methodOutputAbi = contractAbi.find(funcSignature => funcSignature.name === methodData.methodName).outputs; const output = outputs[outputIndex]; if (!output) { throw new errors_1.RubicSdkError('Output has to be defined'); } outputIndex++; return { success: output.success, output: output.success && output.returnData.length > 2 ? this.web3.eth.abi.decodeParameters(methodOutputAbi, output.returnData)[0] : null }; })); } return this.multicallContractsMethodsByOne(contractAbi, contractsData); } /** * Executes multiple calls in the single contract call. * @param calls Multicall calls data list. * @returns Result of calls execution. */ async multicall(calls) { const contract = new this.web3.eth.Contract(evm_multicall_abi_1.EVM_MULTICALL_ABI, this.multicallAddress); return contract.methods.tryAggregate(false, calls).call(); } multicallContractsMethodsByOne(contractAbi, contractsData) { return Promise.all(contractsData.map(contractData => { const contract = new this.web3.eth.Contract(contractAbi, contractData.contractAddress); return Promise.all(contractData.methodsData.map(async (methodData) => { try { const output = (await contract.methods[methodData.methodName](...methodData.methodArguments).call()); return { success: true, output }; } catch { return { success: false, output: null }; } })); })); } async callContractMethod(contractAddress, contractAbi, methodName, methodArguments = [], options = {}) { const contract = new this.web3.eth.Contract(contractAbi, contractAddress); return contract.methods[methodName](...methodArguments).call({ ...(options.from && { from: options.from }), ...(options.value && { value: options.value }) }); } /** * Predicts the volume of gas required to execute the contract method. * @param contractAbi Abi of smart-contract. * @param contractAddress Address of smart-contract. * @param methodName Method which execution gas limit is to be calculated. * @param methodArguments Arguments of the contract method. * @param fromAddress The address for which the gas calculation will be called. * @param value The value transferred for the call “transaction” in wei. * @returns Estimated gas limit. */ async getEstimatedGas(contractAbi, contractAddress, methodName, methodArguments, fromAddress, value) { const contract = new this.web3.eth.Contract(contractAbi, contractAddress); try { const gasLimit = await contract.methods[methodName](...methodArguments).estimateGas({ from: fromAddress, gas: 10000000, ...(value && { value }) }); return new bignumber_js_1.default(gasLimit); } catch (err) { console.debug(err); return null; } } /** * Get estimated gas of several contract method executions via rpc batch request. * @param fromAddress Sender address. * @param callsData Transactions parameters. * @returns List of contract execution estimated gases. * If the execution of the method in the real blockchain would not be reverted, * then the list item would be equal to the predicted gas limit. * Else (if you have not enough balance, allowance ...) then the list item would be equal to null. */ async batchEstimatedGas(fromAddress, callsData) { try { const rpcCallsData = callsData.map(callData => ({ rpcMethod: 'eth_estimateGas', params: { from: fromAddress, to: callData.to, data: callData.data, ...(callData.value && { value: `0x${parseInt(callData.value).toString(16)}` }) } })); const result = await this.rpcBatchRequest(rpcCallsData); return result.map(value => (value ? new bignumber_js_1.default(value) : null)); } catch (e) { console.error(e); return callsData.map(() => null); } } /** * Sends batch request to rpc provider directly. * @see {@link https://playground.open-rpc.org/?schemaUrl=https://raw.githubusercontent.com/ethereum/eth1.0-apis/assembled-spec/openrpc.json&uiSchema%5BappBar%5D%5Bui:splitView%5D=false&uiSchema%5BappBar%5D%5Bui:input%5D=false&uiSchema%5BappBar%5D%5Bui:examplesDropdown%5D=false|EthereumJSON-RPC} * @param rpcCallsData Rpc methods and parameters list. * @returns Rpc batch request call result sorted in order of input parameters. */ async rpcBatchRequest(rpcCallsData) { const seed = Date.now(); const batch = rpcCallsData.map((callData, index) => ({ id: seed + index, jsonrpc: '2.0', method: callData.rpcMethod, params: [{ ...callData.params }] })); const httpClient = await this.getHttpClient(); const response = await httpClient.post(this.web3.currentProvider.host, batch); return response.sort((a, b) => a.id - b.id).map(item => (item.error ? null : item.result)); } /** * Returns httpClient if it exists or imports the axios client. */ async getHttpClient() { if (!this.httpClient) { this.httpClient = await default_http_client_1.DefaultHttpClient.getInstance(); } return this.httpClient; } /** * Gets mined transaction receipt. * @param hash Transaction hash */ async getTransactionReceipt(hash) { return this.web3.eth.getTransactionReceipt(hash); } async getTransactionStatus(hash) { const txReceipt = await this.getTransactionReceipt(hash); if (txReceipt === null) { return tx_status_1.TxStatus.PENDING; } if (txReceipt.status) { return tx_status_1.TxStatus.SUCCESS; } return tx_status_1.TxStatus.FAIL; } /** * Calculates the average price per unit of gas according to web3. * @returns Average gas price in wei. */ async getGasPrice() { return this.web3.eth.getGasPrice(); } /** * Gets block by block id. * @param [blockId] Block id: hash, number ... Default is 'latest'. * @returns Block by blockId parameter. */ getBlock(blockId = 'latest') { return this.web3.eth.getBlock(blockId); } async getBlockNumber() { return this.web3.eth.getBlockNumber(); } async getPastEvents(contractAddress, contractAbi, eventName, options) { const contract = new this.web3.eth.Contract(contractAbi, contractAddress); const blockNumber = options.toBlock === 'latest' ? await this.getBlockNumber() : options.toBlock; return contract.getPastEvents(eventName, { fromBlock: blockNumber - options.blocksAmount, toBlock: blockNumber }); } /** * Will call smart contract method in the EVM without sending any transaction. * @param contractAddress Contract address. * @param contractAbi Contract ABI. * @param methodName Method name. * @param methodArguments Method arguments. * @param options Sender address and value. * @returns Transaction receipt. */ async staticCallContractMethod(contractAddress, contractAbi, methodName, methodArguments, options) { const contract = new this.web3.eth.Contract(contractAbi, contractAddress); return new Promise((resolve, reject) => { contract.methods[methodName](...methodArguments).call({ from: options?.from, ...(options?.value && { value: options.value }) }, (error, result) => { if (result) { resolve(result); } if (error) { reject(error); } }); }); } } exports.EvmWeb3Public = EvmWeb3Public; //# sourceMappingURL=evm-web3-public.js.map