rubic-sdk
Version:
Simplify dApp creation
324 lines • 15.3 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EvmOnChainTrade = void 0;
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const errors_1 = require("../../../../../../../common/errors");
const native_tokens_1 = require("../../../../../../../common/tokens/constants/native-tokens");
const errors_2 = require("../../../../../../../common/utils/errors");
const blockchain_name_1 = require("../../../../../../../core/blockchain/models/blockchain-name");
const blockchains_info_1 = require("../../../../../../../core/blockchain/utils/blockchains-info/blockchains-info");
const blockchain_id_1 = require("../../../../../../../core/blockchain/utils/blockchains-info/constants/blockchain-id");
const evm_web3_pure_1 = require("../../../../../../../core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/evm-web3-pure");
const web3_pure_1 = require("../../../../../../../core/blockchain/web3-pure/web3-pure");
const injector_1 = require("../../../../../../../core/injector/injector");
const rubic_proxy_contract_address_1 = require("../../../../../../cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address");
const evm_common_cross_chain_abi_1 = require("../../../../../../cross-chain/calculation-manager/providers/common/evm-cross-chain-trade/constants/evm-common-cross-chain-abi");
const gateway_rubic_cross_chain_abi_1 = require("../../../../../../cross-chain/calculation-manager/providers/common/evm-cross-chain-trade/constants/gateway-rubic-cross-chain-abi");
const proxy_cross_chain_evm_trade_1 = require("../../../../../../cross-chain/calculation-manager/providers/common/proxy-cross-chain-evm-facade/proxy-cross-chain-evm-trade");
const on_chain_trade_1 = require("../on-chain-trade");
const web3_utils_1 = require("web3-utils");
class EvmOnChainTrade extends on_chain_trade_1.OnChainTrade {
get spenderAddress() {
return this.useProxy || this.usedForCrossChain
? rubic_proxy_contract_address_1.rubicProxyContractAddress[this.from.blockchain].gateway
: this.dexContractAddress;
}
get web3Public() {
return injector_1.Injector.web3PublicService.getWeb3Public(this.from.blockchain);
}
get web3Private() {
return injector_1.Injector.web3PrivateService.getWeb3PrivateByBlockchain(this.from.blockchain);
}
constructor(evmOnChainTradeStruct, providerAddress) {
super(providerAddress);
this.lastTransactionConfig = null;
/**
* Filled if approve goes through permit2 contract
*/
this.permit2ApproveConfig = {
usePermit2Approve: false,
permit2Address: null
};
this.from = evmOnChainTradeStruct.from;
this.to = evmOnChainTradeStruct.to;
this.slippageTolerance = evmOnChainTradeStruct.slippageTolerance;
this.path = evmOnChainTradeStruct.path;
this.gasFeeInfo = evmOnChainTradeStruct.gasFeeInfo;
this.useProxy = evmOnChainTradeStruct.useProxy;
this.fromWithoutFee = evmOnChainTradeStruct.fromWithoutFee;
this.usedForCrossChain = evmOnChainTradeStruct.usedForCrossChain || false;
if (evmOnChainTradeStruct.permit2ApproveAddress) {
this.permit2ApproveConfig = {
usePermit2Approve: true,
permit2Address: evmOnChainTradeStruct.permit2ApproveAddress
};
}
this.feeInfo = {
rubicProxy: {
...(evmOnChainTradeStruct.proxyFeeInfo?.fixedFeeToken && {
fixedFee: {
amount: evmOnChainTradeStruct.proxyFeeInfo?.fixedFeeToken.tokenAmount ||
new bignumber_js_1.default(0),
token: evmOnChainTradeStruct.proxyFeeInfo?.fixedFeeToken
}
}),
...(evmOnChainTradeStruct.proxyFeeInfo?.platformFee && {
platformFee: {
percent: evmOnChainTradeStruct.proxyFeeInfo?.platformFee.percent || 0,
token: evmOnChainTradeStruct.proxyFeeInfo?.platformFee.token
}
})
}
};
this.withDeflation = evmOnChainTradeStruct.withDeflation;
}
async approve(options, checkNeedApprove = true, amount = 'infinity') {
if (checkNeedApprove) {
const needApprove = await this.needApprove();
if (!needApprove) {
throw new errors_1.UnnecessaryApproveError();
}
}
this.checkWalletConnected();
await this.checkBlockchainCorrect();
const approveAmount = this.from.blockchain === blockchain_name_1.BLOCKCHAIN_NAME.GNOSIS ||
this.from.blockchain === blockchain_name_1.BLOCKCHAIN_NAME.CRONOS
? this.from.weiAmount
: amount;
const fromTokenAddress = this.from.isNative && this.from.blockchain === blockchain_name_1.BLOCKCHAIN_NAME.METIS
? '0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000'
: this.from.address;
return this.web3Private.approveTokens(fromTokenAddress, this.spenderAddress, approveAmount, options);
}
async encodeApprove(tokenAddress, spenderAddress, value, options = {}) {
return this.web3Private.encodeApprove(tokenAddress, spenderAddress, value, options);
}
async checkAllowanceAndApprove(options) {
const needApprove = await this.needApprove();
if (!needApprove) {
return;
}
const approveOptions = {
onTransactionHash: options?.onApprove,
gas: options?.approveGasLimit || undefined,
gasPriceOptions: options?.gasPriceOptions || undefined
};
await this.approve(approveOptions, false);
}
/**
* Calculates value for swap transaction.
* @param providerValue Value, returned from cross-chain provider.
*/
getSwapValue(providerValue) {
const nativeToken = native_tokens_1.nativeTokensList[this.from.blockchain];
const fixedFeeValue = web3_pure_1.Web3Pure.toWei(this.feeInfo.rubicProxy?.fixedFee?.amount || 0, nativeToken.decimals);
let fromValue;
if (this.from.isNative) {
if (providerValue) {
fromValue = new bignumber_js_1.default(providerValue).dividedBy(1 - (this.feeInfo.rubicProxy?.platformFee?.percent || 0) / 100);
}
else {
fromValue = this.from.weiAmount;
}
}
else {
fromValue = new bignumber_js_1.default(providerValue || 0);
}
return new bignumber_js_1.default(fromValue).plus(fixedFeeValue).toFixed(0, 0);
}
async swap(options = {}) {
await this.checkWalletState(options?.testMode);
await this.checkAllowanceAndApprove(options);
const { onConfirm } = options;
let transactionHash;
const onTransactionHash = (hash) => {
if (onConfirm) {
onConfirm(hash);
}
transactionHash = hash;
};
const fromAddress = this.walletAddress;
const { data, value, to } = await this.encode({ ...options, fromAddress });
const method = options?.testMode ? 'sendTransaction' : 'trySendTransaction';
try {
await this.web3Private[method](to, {
data,
value,
onTransactionHash,
gasPriceOptions: options.gasPriceOptions,
...(options?.useEip155 && {
chainId: `0x${blockchain_id_1.blockchainId[this.from.blockchain].toString(16)}`
})
});
return transactionHash;
}
catch (err) {
if (err instanceof errors_1.FailedToCheckForTransactionReceiptError) {
return transactionHash;
}
throw (0, errors_2.parseError)(err);
}
}
async getData(fromAddress, options = {}) {
this.apiFromAddress = fromAddress;
if (!options?.testMode) {
await this.checkWalletState(options.testMode);
}
await this.checkReceiverAddress(options.receiverAddress, !blockchains_info_1.BlockchainsInfo.isEvmBlockchainName(this.to.blockchain));
const { data, value, to } = await this.encode({ ...options, fromAddress });
try {
if (!options?.testMode) {
const gasfullOptions = await this.web3Private.simulateTransaction(to, {
data,
value
}, this.from.blockchain);
return gasfullOptions;
}
return { data, value, to };
}
catch (err) {
throw err;
}
}
async encode(options) {
await this.checkFromAddress(options.fromAddress, true);
await this.checkReceiverAddress(options.receiverAddress);
if (this.useProxy) {
return this.encodeProxy(options);
}
return this.encodeDirect(options);
}
/**
* Encodes trade to swap it through on-chain proxy.
*/
async encodeProxy(options) {
const { contractAddress, contractAbi, methodName, methodArguments, value } = await this.getProxyContractParams(options);
const gasParams = this.getGasParams(options);
return evm_web3_pure_1.EvmWeb3Pure.encodeMethodCall(contractAddress, contractAbi, methodName, methodArguments, value, gasParams);
}
async getProxyContractParams(options) {
const swapData = await this.getSwapData(options);
const receiverAddress = options.receiverAddress || options.fromAddress;
const methodArguments = [
evm_web3_pure_1.EvmWeb3Pure.randomHex(32),
this.providerAddress,
EvmOnChainTrade.getReferrerAddress(options.referrer),
receiverAddress,
this.toTokenAmountMin.stringWeiAmount,
swapData
];
const nativeToken = native_tokens_1.nativeTokensList[this.from.blockchain];
const proxyFee = new bignumber_js_1.default(this.feeInfo.rubicProxy?.fixedFee?.amount || '0');
const value = web3_pure_1.Web3Pure.toWei(proxyFee.plus(this.from.isNative ? this.from.tokenAmount : '0'), nativeToken.decimals);
const txConfig = evm_web3_pure_1.EvmWeb3Pure.encodeMethodCall(rubic_proxy_contract_address_1.rubicProxyContractAddress[this.from.blockchain].router, evm_common_cross_chain_abi_1.evmCommonCrossChainAbi, 'swapTokensGeneric', methodArguments, value);
const sendingToken = this.from.isNative ? [] : [this.from.address];
const sendingAmount = this.from.isNative ? [] : [this.from.stringWeiAmount];
return {
contractAddress: rubic_proxy_contract_address_1.rubicProxyContractAddress[this.from.blockchain].gateway,
contractAbi: gateway_rubic_cross_chain_abi_1.gatewayRubicCrossChainAbi,
methodName: 'startViaRubic',
methodArguments: [sendingToken, sendingAmount, txConfig.data],
value
};
}
static getReferrerAddress(referrer) {
if (referrer) {
return '0x' + (0, web3_utils_1.utf8ToHex)(referrer).slice(2, 42).padStart(40, '0');
}
return '0x0000000000000000000000000000000000000000';
}
/**
* Encodes trade to swap it directly through dex contract.
* @param options Encode options.
*/
async encodeDirect(options) {
await this.checkFromAddress(options.fromAddress, true);
await this.checkReceiverAddress(options.receiverAddress);
try {
const transactionData = await this.setTransactionConfig(options);
const { gas, gasPrice } = this.getGasParams(options, {
gasLimit: transactionData.gas,
gasPrice: transactionData.gasPrice
});
return {
to: transactionData.to,
data: transactionData.data,
value: this.fromWithoutFee.isNative ? this.fromWithoutFee.stringWeiAmount : '0',
gas,
gasPrice
};
}
catch (err) {
throw this.getSwapError(err);
}
}
isDeflationError() {
return ((this.withDeflation.from.isDeflation || this.withDeflation.to.isDeflation) &&
this.slippageTolerance < 0.12);
}
getGasParams(options, calculatedGasFee = {
gasLimit: this.gasFeeInfo?.gasLimit?.toFixed(),
gasPrice: this.gasFeeInfo?.gasPrice?.toFixed()
}) {
return {
gas: options.gasLimit || calculatedGasFee.gasLimit,
gasPrice: options.gasPrice || calculatedGasFee.gasPrice,
maxPriorityFeePerGas: options.maxPriorityFeePerGas || calculatedGasFee.maxPriorityFeePerGas,
maxFeePerGas: options.maxFeePerGas || calculatedGasFee.maxFeePerGas
};
}
async getSwapData(options) {
const directTransactionConfig = await this.encodeDirect({
...options,
fromAddress: rubic_proxy_contract_address_1.rubicProxyContractAddress[this.from.blockchain].router,
supportFee: false,
receiverAddress: rubic_proxy_contract_address_1.rubicProxyContractAddress[this.from.blockchain].router
});
const availableDexs = (await proxy_cross_chain_evm_trade_1.ProxyCrossChainEvmTrade.getWhitelistedDexes(this.from.blockchain)).map(address => address.toLowerCase());
const routerAddress = directTransactionConfig.to;
const method = directTransactionConfig.data.slice(0, 10);
if (!availableDexs.includes(routerAddress.toLowerCase())) {
throw new errors_1.NotWhitelistedProviderError(routerAddress, undefined, 'dex');
}
await proxy_cross_chain_evm_trade_1.ProxyCrossChainEvmTrade.checkDexWhiteList(this.from.blockchain, routerAddress, method);
return [
[
routerAddress,
routerAddress,
this.from.address,
this.to.address,
this.from.stringWeiAmount,
directTransactionConfig.data,
true
]
];
}
async setTransactionConfig(options) {
if (this.lastTransactionConfig && options.useCacheData) {
return this.lastTransactionConfig;
}
const { tx, toAmount } = await this.getTransactionConfigAndAmount(options);
this.lastTransactionConfig = tx;
setTimeout(() => {
this.lastTransactionConfig = null;
}, 15000);
if (!options.skipAmountCheck) {
this.checkAmountChange(toAmount, this.to.stringWeiAmount);
}
return tx;
}
getSwapError(err) {
if ([400, 500, 503].includes(err.code)) {
throw new errors_1.SwapRequestError();
}
if (this.isDeflationError()) {
throw new errors_1.LowSlippageDeflationaryTokenError();
}
throw (0, errors_2.parseError)(err);
}
}
exports.EvmOnChainTrade = EvmOnChainTrade;
//# sourceMappingURL=evm-on-chain-trade.js.map