UNPKG

rubic-sdk

Version:
241 lines • 10.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DedustSwapService = void 0; const sdk_1 = require("@dedust/sdk"); const core_1 = require("@ton/core"); const bignumber_js_1 = __importDefault(require("bignumber.js")); const errors_1 = require("../../../../../../../common/errors"); const tokens_1 = require("../../../../../../../common/tokens"); const blockchain_name_1 = require("../../../../../../../core/blockchain/models/blockchain-name"); const chain_type_1 = require("../../../../../../../core/blockchain/models/chain-type"); const ton_client_1 = require("../../../../../../../core/blockchain/web3-private-service/web3-private/ton-web3-private/ton-client/ton-client"); const injector_1 = require("../../../../../../../core/injector/injector"); const on_chain_trade_type_1 = require("../../../common/models/on-chain-trade-type"); const dedust_consts_1 = require("../constants/dedust-consts"); const dedust_api_service_1 = require("./dedust-api-service"); const dedust_sender_class_1 = require("./dedust-sender-class"); class DedustSwapService { get mainnetFactoryAddress() { return sdk_1.MAINNET_FACTORY_ADDR; } constructor() { this.txSteps = []; this.tonClient = ton_client_1.TonClientInstance.getInstance(); this.factory = this.tonClient.open(sdk_1.Factory.createFromAddress(this.mainnetFactoryAddress)); } /** * @returns string wei amount */ async calcOutputAmount(from, to) { if (this.maybeMultistepSwap(from, to)) { const pools = await dedust_api_service_1.DedustApiService.findBestPools(from, to); if (!pools.length) { throw new errors_1.RubicSdkError('[DedustSwapService_calcOutputAmount] Pools are not found!'); } this.cacheStepsOnCalculation(pools); console.log('%cDedust_TransitTokensCount', 'color: blue; font-size: 20px;', pools.length - 1); const outputWeiAmountString = pools.at(-1).amountOut; return outputWeiAmountString; } const fromAsset = this.getTokenAsset(from); const pool = await this.getPool(from, to); const { amountOut } = await pool.getEstimatedSwapOut({ assetIn: fromAsset, amountIn: BigInt(from.stringWeiAmount) }); this.cacheStepsOnCalculation([ { amountOut: amountOut.toString(), poolAddress: pool.address, srcTokenAddress: from.address, dstTokenAddress: to.address } ]); return amountOut.toString(); } async sendTransaction(from, to, walletAddress, slippage, onHash) { const web3Private = injector_1.Injector.web3PrivateService.getWeb3Private(chain_type_1.CHAIN_TYPE.TON); const sender = new dedust_sender_class_1.DedustTxSender(walletAddress, web3Private, onHash); const minAmountOut = BigInt(to.weiAmount.multipliedBy(1 - slippage).toFixed(0)); try { if (from.isNative) { await this.swapTonToJetton(from, sender, minAmountOut); } else if (to.isNative) { await this.swapJettonToTon(from, sender, minAmountOut); } else { await this.swapJettonToJetton(from, sender, slippage); } } catch (err) { throw err; } } isMultistepSwap() { return this.txSteps.length > 1; } async getRoutePath() { const promises = this.txSteps.map(async (step) => { const srcToken = tokens_1.Token.createToken({ address: step.srcTokenAddress, blockchain: blockchain_name_1.BLOCKCHAIN_NAME.TON }); const dstToken = tokens_1.Token.createToken({ address: step.dstTokenAddress, blockchain: blockchain_name_1.BLOCKCHAIN_NAME.TON }); return { provider: on_chain_trade_type_1.ON_CHAIN_TRADE_TYPE.DEDUST, type: 'on-chain', path: await Promise.all([srcToken, dstToken]) }; }); const path = await Promise.all(promises); return path; } /** * Stores found pools in calcOutputAmount method and reuses its in sendTransaction methods */ cacheStepsOnCalculation(steps) { this.txSteps = steps; } /** * maybe uses more than 1 pool */ maybeMultistepSwap(from, to) { return !from.isNative && !to.isNative; } async getPool(from, to) { try { const fromAsset = this.getTokenAsset(from); const toAsset = this.getTokenAsset(to); const poolAddress = await this.factory.getPoolAddress({ poolType: sdk_1.PoolType.VOLATILE, assets: [fromAsset, toAsset] }); const pool = this.tonClient.open(sdk_1.Pool.createFromAddress(poolAddress)); const status = await pool.getReadinessStatus(); if (status !== sdk_1.ReadinessStatus.READY) { throw new Error(`[DedustSwapService_getPool] Pool does not exist.`); } return pool; } catch (err) { throw err; } } getTokenAsset(token) { if (token.isNative) return sdk_1.Asset.native(); const parsedAddress = core_1.Address.parse(token.address); const openedTokenContract = this.tonClient.open(sdk_1.JettonRoot.createFromAddress(parsedAddress)); return sdk_1.Asset.jetton(openedTokenContract.address); } async getVault(token) { if (token.isNative) { const nativeVault = this.tonClient.open(await this.factory.getNativeVault()); const status = await nativeVault.getReadinessStatus(); if (status !== sdk_1.ReadinessStatus.READY) { throw new errors_1.RubicSdkError('Vault (TON) does not exist.'); } return nativeVault; } const parsedAddress = core_1.Address.parse(token.address); const jettonVault = this.tonClient.open(await this.factory.getJettonVault(parsedAddress)); const status = await jettonVault.getReadinessStatus(); if (status !== sdk_1.ReadinessStatus.READY) { throw new Error(`Vault (${token.symbol}) does not exist.`); } return jettonVault; } async swapTonToJetton(from, sender, minAmountOut) { const poolAddress = this.txSteps[0].poolAddress; const nativeVault = await this.getVault(from); const fromAmount = (0, core_1.toNano)(from.tokenAmount.toFixed()); await nativeVault.sendSwap(sender, { poolAddress, amount: fromAmount, limit: minAmountOut, gasAmount: (0, core_1.toNano)(dedust_consts_1.DEDUST_GAS_NON_WEI), queryId: dedust_consts_1.RUBIC_REF_NAME_FOR_DEDUST }); } async swapJettonToTon(from, sender, minAmountOut) { const poolAddress = this.txSteps[0].poolAddress; const jettonVault = await this.getVault(from); const parsedAddress = core_1.Address.parse(from.address); const result = await this.tonClient.runMethod(parsedAddress, 'get_wallet_address', [ { type: 'slice', cell: (0, core_1.beginCell)().storeAddress(sender.address).endCell() } ]); const jettonWalletAddress = result.stack.readAddress(); const jettonWallet = this.tonClient.open(sdk_1.JettonWallet.createFromAddress(jettonWalletAddress)); await jettonWallet.sendTransfer(sender, (0, core_1.toNano)(dedust_consts_1.DEDUST_GAS_NON_WEI), { amount: BigInt(from.stringWeiAmount), destination: jettonVault.address, responseAddress: sender.address, forwardAmount: (0, core_1.toNano)(0.15), queryId: dedust_consts_1.RUBIC_REF_NAME_FOR_DEDUST, forwardPayload: sdk_1.VaultJetton.createSwapPayload({ poolAddress, limit: minAmountOut }) }); } async swapJettonToJetton(from, sender, slippage) { const jettonVault = await this.getVault(from); const parsedAddress = core_1.Address.parse(from.address); const jettonRoot = this.tonClient.open(sdk_1.JettonRoot.createFromAddress(parsedAddress)); const jettonWallet = this.tonClient.open(await jettonRoot.getWallet(sender.address)); const payloadParams = {}; this.fillPayloadParams(this.txSteps, slippage, payloadParams); console.log('%cDedust_Params', 'color: green; font-size: 20px;', payloadParams); if (!this.checkSwapPayloadValid(payloadParams)) { console.log('%cInvalid_swapPayloadParams', 'color: red; font-size: 20px;', payloadParams); throw new errors_1.RubicSdkError('Swap payload for dedust has empty `next` property or undefined `poolAddress`.'); } await jettonWallet.sendTransfer(sender, (0, core_1.toNano)(dedust_consts_1.DEDUST_GAS_NON_WEI), { amount: BigInt(from.stringWeiAmount), destination: jettonVault.address, responseAddress: sender.address, forwardAmount: (0, core_1.toNano)(0.15), queryId: dedust_consts_1.RUBIC_REF_NAME_FOR_DEDUST, forwardPayload: sdk_1.VaultJetton.createSwapPayload(payloadParams) }); } /** * @param next on first iteration - it's payloadParams, then .next * @param payloadParams * @returns */ fillPayloadParams(steps, slippage, next) { if (!steps.length) return; const step = steps[0]; const minAmountOut = BigInt(new bignumber_js_1.default(step.amountOut).multipliedBy(1 - slippage).toFixed(0)); next.poolAddress = step.poolAddress; next.limit = minAmountOut; steps.shift(); if (steps.length) { next.next = {}; next = next.next; } return this.fillPayloadParams(steps, slippage, next); } checkSwapPayloadValid(payloadParams) { let next = payloadParams; while (next) { if (!next.poolAddress) return false; if (next.next && !Object.keys(next.next).length) return false; next = next.next; } return true; } } exports.DedustSwapService = DedustSwapService; //# sourceMappingURL=dedust-swap-service.js.map