-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6339538
commit 234c513
Showing
13 changed files
with
306 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,12 @@ | ||
export const defaultBaseUrl = 'https://app.mytonswap.com/api/'; | ||
export const TON_ADDRESS = 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c'; | ||
export const supportedMintlessTokens = [ | ||
'EQD6Z9DHc5Mx-8PI8I4BjGX0d2NhapaRAK12CgstweNoMint', | ||
'EQAJ8uWd7EBqsmpSWaRdf_I-8R8-XHwh3gsNKhy-UrdrPcUo', | ||
]; | ||
export const feeWallet = '0:3729d9b29010b40af162fc28a6b0917385b59ea54af3fd411c9f5c91d12c31b5'; | ||
export const STON_ROUTER_V1 = 'EQB3ncyBUTjZUA5EnFKR5_EnOMI9V1tTEAAPaiU71gc4TiUt'; | ||
export const PTON_V1 = 'EQCM3B12QK1e4yZSf8GtBRT0aLMNyEsBc_DhVfRRtOEffLez'; | ||
export const PTON_V2 = 'EQBnGWMCf3-FZZq1W4IWcWiGAc3PHuZ0_H-7sad2oY00o83S'; | ||
export const DEDUST_TRANSFER = 0xf8a7ea5; | ||
export const DEDUST_SWAP = 0xea06185d; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
import { address, Cell, SenderArguments, toNano } from '@ton/ton'; | ||
import { | ||
feeWallet, | ||
PTON_V2, | ||
STON_ROUTER_V1, | ||
supportedMintlessTokens, | ||
TON_ADDRESS, | ||
} from '../../constants'; | ||
import { Services } from '../../core/services'; | ||
import { BestRoute } from '../../types/router'; | ||
import { Balance, CustomPayload } from '../../types/swap'; | ||
import { DEX, pTON } from '@ston-fi/sdk'; | ||
|
||
export class Swap extends Services { | ||
/** | ||
* swap | ||
*/ | ||
public async swap(userWalletAddress: string, bestRoute: BestRoute) { | ||
switch (bestRoute.selected_pool.dex) { | ||
case 'stonfi': | ||
return await this.createStonSwap(userWalletAddress, bestRoute); | ||
case 'dedust': | ||
break; | ||
default: | ||
break; | ||
} | ||
} | ||
|
||
/** | ||
* createStonSwap | ||
* | ||
*/ | ||
private async createStonSwap(userWalletAddress: string, bestRoute: BestRoute) { | ||
let jettonData: Balance | undefined; | ||
let swapTxParams: SenderArguments | null = null; | ||
const isMintless = supportedMintlessTokens.includes(bestRoute.selected_pool.token0_address); | ||
if (isMintless) { | ||
jettonData = await this.client.tonapi.getJettonData( | ||
userWalletAddress, | ||
bestRoute.selected_pool.token0_address, | ||
); | ||
} | ||
|
||
let customPayload: Cell | undefined; | ||
let stateInit: { code: Cell; data: Cell } | undefined; | ||
|
||
if (isMintless && jettonData?.extensions?.includes('custom_payload')) { | ||
const offerJettonCustomPayload = await this.client.tonapi.getCustomPayload( | ||
userWalletAddress, | ||
bestRoute.selected_pool.token0_address, | ||
); | ||
|
||
if (!offerJettonCustomPayload) { | ||
throw new Error('Unable to retrieve custom payload. Please try again.'); | ||
} | ||
customPayload = Cell.fromBoc( | ||
Buffer.from(offerJettonCustomPayload.custom_payload, 'hex'), | ||
)[0]; | ||
const stateInitCell = Cell.fromBoc( | ||
Buffer.from(offerJettonCustomPayload.state_init, 'hex'), | ||
)[0].beginParse(); | ||
|
||
stateInit = { | ||
code: stateInitCell.loadRef(), | ||
data: stateInitCell.loadRef(), | ||
}; | ||
} | ||
|
||
const router = | ||
bestRoute.selected_pool.router_address === STON_ROUTER_V1 | ||
? this.client.tonClient.open( | ||
DEX.v1.Router.create(bestRoute.selected_pool.router_address), | ||
) | ||
: this.client.tonClient.open( | ||
DEX.v2_1.Router.create(bestRoute.selected_pool.router_address), | ||
); | ||
|
||
const pTon = | ||
bestRoute.selected_pool.router_address === STON_ROUTER_V1 | ||
? new pTON.v1() | ||
: new pTON.v2_1(PTON_V2); | ||
const gasConstants = | ||
bestRoute.selected_pool.router_address === STON_ROUTER_V1 | ||
? DEX.v1.Router.gasConstants | ||
: DEX.v2_1.Router.gasConstants; | ||
|
||
console.log(bestRoute.pool_data.route[0]); | ||
if (bestRoute.pool_data.route[0] === TON_ADDRESS) { | ||
swapTxParams = await router.getSwapTonToJettonTxParams({ | ||
userWalletAddress: userWalletAddress, | ||
proxyTon: pTon, | ||
offerAmount: bestRoute.pool_data.pay, | ||
askJettonAddress: bestRoute.pool_data.route[1], | ||
minAskAmount: BigInt(bestRoute.pool_data.minimumReceive), | ||
referralAddress: address(feeWallet), | ||
}); | ||
} else if (bestRoute.pool_data.route[1] === TON_ADDRESS) { | ||
swapTxParams = await router.getSwapJettonToTonTxParams({ | ||
userWalletAddress: userWalletAddress, | ||
offerJettonAddress: bestRoute.pool_data.route[0], | ||
offerAmount: bestRoute.pool_data.pay, | ||
proxyTon: pTon, | ||
jettonCustomPayload: customPayload ? customPayload : undefined, | ||
minAskAmount: BigInt(bestRoute.pool_data.minimumReceive), | ||
referralAddress: feeWallet, | ||
gasAmount: isMintless | ||
? gasConstants.swapJettonToJetton.gasAmount + toNano(0.1) | ||
: undefined, | ||
}); | ||
} else { | ||
swapTxParams = await router.getSwapJettonToJettonTxParams({ | ||
userWalletAddress: userWalletAddress, | ||
offerJettonAddress: bestRoute.selected_pool.token0_address, | ||
offerAmount: BigInt(bestRoute.pool_data.pay), | ||
askJettonAddress: bestRoute.selected_pool.token1_address, | ||
jettonCustomPayload: customPayload ? customPayload : undefined, | ||
|
||
minAskAmount: BigInt(bestRoute.pool_data.minimumReceive), | ||
referralAddress: address(feeWallet), | ||
gasAmount: stateInit | ||
? gasConstants.swapJettonToJetton.gasAmount + toNano(0.1) | ||
: undefined, | ||
}); | ||
} | ||
return swapTxParams; | ||
} | ||
|
||
private async createDedustSwap(userWalletAddress: string, bestRoute: BestRoute) { | ||
let [asset0, asset1, asset2] = bestRoute.pool_data.route; | ||
let jettonData: Balance | undefined; | ||
let swapTxParams: SenderArguments | null = null; | ||
const isMintless = supportedMintlessTokens.includes(bestRoute.selected_pool.token0_address); | ||
if (isMintless) { | ||
jettonData = await this.client.tonapi.getJettonData( | ||
userWalletAddress, | ||
bestRoute.selected_pool.token0_address, | ||
); | ||
} | ||
|
||
let customPayload: Cell | undefined; | ||
let stateInit: { code: Cell; data: Cell } | undefined; | ||
|
||
if (isMintless && jettonData?.extensions?.includes('custom_payload')) { | ||
const offerJettonCustomPayload = await this.client.tonapi.getCustomPayload( | ||
userWalletAddress, | ||
bestRoute.selected_pool.token0_address, | ||
); | ||
|
||
if (!offerJettonCustomPayload) { | ||
throw new Error('Unable to retrieve custom payload. Please try again.'); | ||
} | ||
customPayload = Cell.fromBoc( | ||
Buffer.from(offerJettonCustomPayload.custom_payload, 'hex'), | ||
)[0]; | ||
const stateInitCell = Cell.fromBoc( | ||
Buffer.from(offerJettonCustomPayload.state_init, 'hex'), | ||
)[0].beginParse(); | ||
|
||
stateInit = { | ||
code: stateInitCell.loadRef(), | ||
data: stateInitCell.loadRef(), | ||
}; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { beforeAll, beforeEach, expect, test } from 'bun:test'; | ||
import { MyTonSwapClient } from '../../core/client'; | ||
import { toNano } from '@ton/ton'; | ||
let client: MyTonSwapClient; | ||
const userWallet = 'UQAaIQh7jVlOEylI8jM8OI1O-yYGZRqUfJRxuR-K57pskl9I'; | ||
const hmstrJetton = 'EQAJ8uWd7EBqsmpSWaRdf_I-8R8-XHwh3gsNKhy-UrdrPcUo'; | ||
beforeEach(() => { | ||
client = new MyTonSwapClient(); | ||
}); | ||
|
||
test('it should get data for swap from stonfi', async () => { | ||
const TON = await client.assets.getExactAsset('TON'); | ||
const NOT = await client.assets.getExactAsset('NOT'); | ||
const bestRoute = await client.router.findBestRoute(TON!.address, NOT!.address, 1, 1, 'stonfi'); | ||
const swap = await client.swap.swap(userWallet, bestRoute); | ||
expect(swap).toBeObject(); | ||
expect(swap?.value).not.toBeUndefined(); | ||
expect(swap?.value).toBeGreaterThan(toNano(1)); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { Services } from '../../core/services'; | ||
import { Balance, CustomPayload } from '../../types/swap'; | ||
|
||
export class TonApi extends Services { | ||
public async getJettonData(walletAddr: string, jettonAddress: string) { | ||
const data = await this.client.request.send<Balance>({ | ||
baseURL: 'https://tonapi.io/v2', | ||
maxBodyLength: Infinity, | ||
url: `/accounts/${walletAddr}/jettons/${jettonAddress}?supported_extensions=custom_payload`, | ||
}); | ||
|
||
return data; | ||
} | ||
|
||
public async getCustomPayload(walletAddr: string, jettonAddress: string) { | ||
const data = await this.client.request.send<CustomPayload>({ | ||
baseURL: 'https://tonapi.io/v2', | ||
maxBodyLength: Infinity, | ||
url: `/jettons/${jettonAddress}/transfer/${walletAddr}/payload`, | ||
}); | ||
|
||
return data; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { beforeAll, beforeEach, expect, test } from 'bun:test'; | ||
import { MyTonSwapClient } from '../../core/client'; | ||
let client: MyTonSwapClient; | ||
const userWallet = 'UQAaIQh7jVlOEylI8jM8OI1O-yYGZRqUfJRxuR-K57pskl9I'; | ||
const hmstrJetton = 'EQAJ8uWd7EBqsmpSWaRdf_I-8R8-XHwh3gsNKhy-UrdrPcUo'; | ||
beforeEach(() => { | ||
client = new MyTonSwapClient(); | ||
}); | ||
|
||
test('it should get jetton with custom payload uri', async () => { | ||
const data = await client.tonapi.getJettonData(userWallet, hmstrJetton); | ||
expect(data.jetton.custom_payload_api_uri).not.toBeUndefined(); | ||
}); | ||
|
||
test('it should get fail on random address', async () => { | ||
expect(async () => await client.tonapi.getJettonData(userWallet, 'hmstrJetton')).toThrow(); | ||
}); | ||
|
||
test('it should get custom payload for Hamster Kombat Token', async () => { | ||
const customPayload = await client.tonapi.getCustomPayload(userWallet, hmstrJetton); | ||
expect(customPayload.custom_payload).not.toBeUndefined(); | ||
expect(customPayload.state_init).not.toBeUndefined(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
export type MyTonSwapClientOptions = { | ||
apiKey: string; | ||
tonCenterApiKey: string; | ||
baseUrl: string; | ||
}; | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
export interface Balance { | ||
balance: string; | ||
wallet_address: WalletAddress; | ||
jetton: Jetton; | ||
extensions?: string[]; | ||
} | ||
|
||
export interface WalletAddress { | ||
address: string; | ||
is_scam: boolean; | ||
is_wallet: boolean; | ||
} | ||
|
||
export interface Jetton { | ||
address: string; | ||
name: string; | ||
symbol: string; | ||
decimals: number; | ||
image: string; | ||
verification: string; | ||
prices?: Prices; | ||
custom_payload_api_uri?: string; | ||
} | ||
export interface Prices { | ||
USD: number; | ||
} | ||
|
||
export interface CustomPayload { | ||
custom_payload: string; | ||
state_init: string; | ||
} | ||
export interface CustomPayload { | ||
custom_payload: string; | ||
state_init: string; | ||
} |