rubic-sdk
Version:
Simplify dApp creation
241 lines • 10.7 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.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