rubic-sdk
Version:
Simplify dApp creation
376 lines • 17.7 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EvmWeb3Private = void 0;
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const errors_1 = require("../../../../../common/errors");
const errors_2 = require("../../../../../common/utils/errors");
const options_1 = require("../../../../../common/utils/options");
const blockchain_name_1 = require("../../../models/blockchain-name");
const blockchains_info_1 = require("../../../utils/blockchains-info/blockchains-info");
const web3_private_1 = require("../web3-private");
const erc_20_token_abi_1 = require("../../../web3-public-service/web3-public/evm-web3-public/constants/erc-20-token-abi");
const uni_v3_permit2_abi_1 = require("../../../web3-public-service/web3-public/evm-web3-public/constants/uni-v3-permit2-abi");
const evm_web3_pure_1 = require("../../../web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure");
const injector_1 = require("../../../../injector/injector");
const proxy_hash_errors_1 = require("../../../../../features/cross-chain/calculation-manager/providers/common/constants/proxy-hash-errors");
class EvmWeb3Private extends web3_private_1.Web3Private {
/**
* Parses web3 error by its code or message.
* @param err Web3 error to parse.
*/
static parseError(err) {
if (err.message.includes('Transaction has been reverted by the EVM')) {
return new errors_1.TransactionRevertedError();
}
if (err.message.includes('execution reverted: UNIV3R: min return')) {
return new errors_1.LowSlippageError();
}
if (err.message.includes('execution reverted: Address: low-level call with value failed') ||
err.message.includes('Low native value')) {
return new errors_1.InsufficientFundsGasPriceValueError();
}
if (err.message.includes('Failed to check for transaction receipt')) {
return new errors_1.FailedToCheckForTransactionReceiptError();
}
if (err.code === -32603) {
return new errors_1.LowGasError();
}
if (err.code === 4001) {
return new errors_1.UserRejectError();
}
try {
const error = EvmWeb3Private.tryParseProxyError(err);
if (error) {
return error;
}
if (err.message.includes('0x6c544f')) {
return new errors_1.InsufficientFundsGasPriceValueError();
}
if (err.message.includes('0xf32bec2f') ||
err.message.includes('execution reverted: Received amount of tokens are less then expected') ||
err.message.includes('0x275c273c')) {
return new errors_1.LowSlippageDeflationaryTokenError();
}
const errorMessage = JSON.parse(err.message.slice(24)).message;
if (errorMessage) {
return new Error(errorMessage);
}
}
catch { }
return (0, errors_2.parseError)(err);
}
static tryParseProxyError(err) {
if ('data' in err) {
const error = proxy_hash_errors_1.proxyHashErrors.find(error => error.hash === err.data);
if (error) {
return new errors_1.RubicSdkError(error.text);
}
}
return undefined;
}
constructor(walletProviderCore) {
super(walletProviderCore.address);
this.Web3Pure = evm_web3_pure_1.EvmWeb3Pure;
this.web3 = walletProviderCore.core;
this.checkAddressCorrect();
}
async getBlockchainName() {
const userChainId = await this.web3.eth.getChainId();
return blockchains_info_1.BlockchainsInfo.getBlockchainNameById(userChainId);
}
/**
* Sends Eth in transaction and resolve the promise when the transaction is included in the block.
* @param toAddress Eth receiver address.
* @param [options] Additional options.
* @returns Transaction receipt.
*/
async sendTransaction(toAddress, options = {}) {
return new Promise((resolve, reject) => {
this.web3.eth
.sendTransaction({
from: this.address,
to: toAddress,
value: web3_private_1.Web3Private.stringifyAmount(options.value || 0),
...(options.gas && { gas: web3_private_1.Web3Private.stringifyAmount(options.gas) }),
...(0, options_1.getGasOptions)(options),
...(options.data && { data: options.data }),
...(options.chainId && { chainId: options.chainId })
})
.on('transactionHash', options.onTransactionHash || (() => { }))
.on('receipt', receipt => resolve(receipt))
.on('error', err => {
console.error(`Send transaction error. ${err}`);
reject(EvmWeb3Private.parseError(err));
});
});
}
/**
* Tries to send Eth in transaction and resolve the promise when the transaction is included in the block or rejects the error.
* @param toAddress Eth receiver address.
* @param [options] Additional options.
* @returns Transaction receipt.
*/
async trySendTransaction(toAddress, options) {
try {
// @TODO use simulate instead
const gaslessParams = {
from: this.address,
to: toAddress,
value: web3_private_1.Web3Private.stringifyAmount(options.value || 0),
...(options.data && { data: options.data }),
...(options?.chainId && { chainId: options.chainId })
};
const gas = await this.web3.eth.estimateGas(gaslessParams);
const gasfulParams = {
...gaslessParams,
...(0, options_1.getGasOptions)(options),
gas: web3_private_1.Web3Private.stringifyAmount(gas, options?.gasLimitRatio || 1.05)
};
try {
await this.web3.eth.estimateGas(gasfulParams);
}
catch {
throw new errors_1.RubicSdkError('Low native value');
}
const sendParams = {
...options,
...gasfulParams
};
return this.sendTransaction(toAddress, sendParams);
}
catch (err) {
console.debug('Call tokens transfer error', err);
const shouldIgnore = await this.shouldIgnoreError(err);
if (shouldIgnore) {
return this.sendTransaction(toAddress, options);
}
throw EvmWeb3Private.parseError(err);
}
}
/**
* Executes method of smart-contract and resolve the promise when the transaction is included in the block.
* @param contractAddress Address of smart-contract which method is to be executed.
* @param contractAbi Abi of smart-contract which method is to be executed.
* @param methodName Method name to execute.
* @param methodArguments Method arguments.
* @param [options] Additional options.
* @returns Smart-contract method returned value.
*/
async executeContractMethod(contractAddress, contractAbi, methodName, methodArguments, options = {}) {
const contract = new this.web3.eth.Contract(contractAbi, contractAddress);
return new Promise((resolve, reject) => {
contract.methods[methodName](...methodArguments)
.send({
from: this.address,
...(options.value && {
value: web3_private_1.Web3Private.stringifyAmount(options.value)
}),
...(options.gas && {
gas: web3_private_1.Web3Private.stringifyAmount(options.gas)
}),
...(0, options_1.getGasOptions)(options),
...(options.chainId && { chainId: options.chainId })
})
.on('transactionHash', options.onTransactionHash || (() => { }))
.on('receipt', resolve)
.on('error', (err) => {
console.error(`Method execution error. ${err}`);
reject(EvmWeb3Private.parseError(err));
});
});
}
/**
* Tries to execute method of smart-contract and resolve the promise when the transaction is included in the block or rejects the error.
* @param contractAddress Address of smart-contract which method is to be executed.
* @param contractAbi Abi of smart-contract which method is to be executed.
* @param methodName Method name to execute.
* @param methodArguments Method arguments.
* @param [options] Additional options.
* @param allowError Check error and decides to execute contact if error is allowed.
*/
async tryExecuteContractMethod(contractAddress, contractAbi, methodName, methodArguments, options = {}, allowError) {
const contract = new this.web3.eth.Contract(contractAbi, contractAddress);
try {
const gaslessParams = {
from: this.address,
...(options.value && { value: web3_private_1.Web3Private.stringifyAmount(options.value) }),
...(options.chainId && { chainId: options.chainId })
};
const gas = await contract.methods[methodName](...methodArguments).estimateGas(gaslessParams);
const gasfulParams = {
...gaslessParams,
...(0, options_1.getGasOptions)(options),
gas: web3_private_1.Web3Private.stringifyAmount(gas, 1.05)
};
try {
await contract.methods[methodName](...methodArguments).estimateGas(gasfulParams);
}
catch {
throw new errors_1.RubicSdkError('Low native value');
}
const sendParams = {
...options,
...gasfulParams
};
return this.executeContractMethod(contractAddress, contractAbi, methodName, methodArguments, sendParams);
}
catch (err) {
if ((allowError && allowError(err)) ||
(await this.shouldIgnoreError(err))) {
return this.executeContractMethod(contractAddress, contractAbi, methodName, methodArguments, options);
}
console.error('Method execution error: ', err);
throw EvmWeb3Private.parseError(err);
}
}
async shouldIgnoreError(error) {
if (error.message === 'Low native value' &&
(await this.getBlockchainName()) === blockchain_name_1.BLOCKCHAIN_NAME.MANTLE) {
return true;
}
const ignoreCallErrors = [
'STF',
'execution reverted: ERC20: transfer amount exceeds allowance',
'Anyswaperc20: request exceed allowance',
'gas required exceeds allowance',
'execution reverted: SafeERC20: low-level call failed'
];
const test = ignoreCallErrors.some(err => error?.message?.toLowerCase().includes(err.toLowerCase()));
console.debug(test);
return test;
}
/**
* Executes approve method in ERC-20 token contract.
* @param tokenAddress Address of the smart-contract corresponding to the token.
* @param spenderAddress Wallet or contract address to approve.
* @param amount Token amount to approve in wei.
* @param [options] Additional options.
* @returns Approval transaction receipt.
*/
async approveTokens(tokenAddress, spenderAddress, amount = 'infinity', options = {}) {
const contract = new this.web3.eth.Contract(erc_20_token_abi_1.ERC20_TOKEN_ABI, tokenAddress);
const rawValue = amount === 'infinity' ? new bignumber_js_1.default(2).pow(256).minus(1) : amount;
const gaslessParams = { from: this.address };
const gas = await contract.methods
.approve(spenderAddress, rawValue.toFixed(0))
.estimateGas(gaslessParams);
const gasfullParams = {
...gaslessParams,
...(0, options_1.getGasOptions)(options),
gas: web3_private_1.Web3Private.stringifyAmount(gas, 1)
};
try {
await contract.methods
.approve(spenderAddress, rawValue.toFixed(0))
.estimateGas(gasfullParams);
}
catch (err) {
if (err?.message?.includes('gas required exceeds allowance')) {
throw err;
}
console.error(err);
}
return new Promise((resolve, reject) => {
contract.methods
.approve(spenderAddress, rawValue.toFixed(0))
.send(gasfullParams)
.on('transactionHash', options.onTransactionHash || (() => { }))
.on('receipt', resolve)
.on('error', (err) => {
console.error(`Tokens approve error. ${err}`);
reject(EvmWeb3Private.parseError(err));
});
});
}
/**
* @param tokenAddress Token address you want to approve for spending
* @param permit2Address Addres of permit2 contract
* @param spenderAddress Contract address spending your tokens
* @param amount Approved amount
* @param deadline Ms number added to current time (Date.now()) until approve expiration
*/
async approveOnPermit2(tokenAddress, permit2Address, spenderAddress, amount = 'infinity', deadline = new bignumber_js_1.default(1000000), options = {}) {
const contract = new this.web3.eth.Contract(uni_v3_permit2_abi_1.UNI_V3_PERMIT_2_ABI, permit2Address);
const rawValue = amount === 'infinity' ? new bignumber_js_1.default(2).pow(256).minus(1) : amount;
const gaslessParams = { from: this.address };
const expiration = new bignumber_js_1.default(Date.now()).plus(deadline).toFixed();
const gas = await contract.methods['approve'](tokenAddress, spenderAddress, rawValue.toFixed(), expiration).estimateGas();
const gasfullParams = {
...gaslessParams,
...(0, options_1.getGasOptions)(options),
gas: web3_private_1.Web3Private.stringifyAmount(gas, 1)
};
return new Promise((resolve, reject) => {
contract.methods['approve'](tokenAddress, spenderAddress, rawValue.toFixed(), expiration)
.send(gasfullParams)
.on('receipt', resolve)
.on('error', (err) => {
console.error(`Tokens approve error. ${err}`);
reject(EvmWeb3Private.parseError(err));
});
});
}
/**
* Build encoded approve transaction config.
* @param tokenAddress Address of the smart-contract corresponding to the token.
* @param spenderAddress Wallet or contract address to approve.
* @param value Amount of tokens in approval window in spending cap field
* @param [options] Additional options.
* @returns Encoded approve transaction config.
*/
async encodeApprove(tokenAddress, spenderAddress, value, options = {}) {
const rawValue = value === 'infinity' ? new bignumber_js_1.default(2).pow(256).minus(1) : value;
const contract = new this.web3.eth.Contract(erc_20_token_abi_1.ERC20_TOKEN_ABI, tokenAddress);
const gaslessParams = { from: this.address };
const gas = await contract.methods
.approve(spenderAddress, rawValue.toFixed(0))
.estimateGas(gaslessParams);
const gasfullParams = {
...gaslessParams,
...(0, options_1.getGasOptions)(options),
gas: web3_private_1.Web3Private.stringifyAmount(gas)
};
await contract.methods
.approve(spenderAddress, rawValue.toFixed(0))
.estimateGas(gasfullParams);
return evm_web3_pure_1.EvmWeb3Pure.encodeMethodCall(tokenAddress, erc_20_token_abi_1.ERC20_TOKEN_ABI, 'approve', [spenderAddress, rawValue.toFixed(0)], undefined, gasfullParams);
}
async simulateTransaction(toAddress, options, blockchain) {
try {
const web3 = this.web3 ?? injector_1.Injector.web3PublicService.getWeb3Public(blockchain).web3Provider;
const gaslessParams = {
from: this.address,
to: toAddress,
value: web3_private_1.Web3Private.stringifyAmount(options.value || 0),
...(options.data && { data: options.data }),
...(options?.chainId && { chainId: options.chainId })
};
const gas = await web3.eth.estimateGas(gaslessParams);
const gasfulParams = {
...gaslessParams,
...(0, options_1.getGasOptions)(options),
gas: web3_private_1.Web3Private.stringifyAmount(gas, options?.gasLimitRatio || 1.05)
};
try {
await web3.eth.estimateGas(gasfulParams);
return gasfulParams;
}
catch {
throw new errors_1.RubicSdkError('Low native value');
}
}
catch (err) {
console.debug('Call tokens transfer error', err);
throw EvmWeb3Private.parseError(err);
}
}
async signMessage(message) {
const signature = this.web3.eth.personal.sign(message, this.address, '');
return signature;
}
}
exports.EvmWeb3Private = EvmWeb3Private;
//# sourceMappingURL=evm-web3-private.js.map