Skip to content

Commit 621d83e

Browse files
committed
refactor: createSwap logic moved to api
1 parent 26cb412 commit 621d83e

File tree

4 files changed

+28
-277
lines changed

4 files changed

+28
-277
lines changed

src/constants/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export const defaultBaseUrl = 'https://app.mytonswap.com/api/';
1+
export const defaultBaseUrl = process.env.BASE_URL ?? 'https://app.mytonswap.com/api/';
22
export const TON_ADDRESS = 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c';
33
export const supportedMintlessTokens = [
44
'EQD6Z9DHc5Mx-8PI8I4BjGX0d2NhapaRAK12CgstweNoMint',

src/services/swap/swap.service.ts

Lines changed: 14 additions & 274 deletions
Original file line numberDiff line numberDiff line change
@@ -18,286 +18,26 @@ import {
1818
} from '../../constants';
1919
import { Services } from '../../core/services';
2020
import { BestRoute } from '../../types/router';
21-
import { Balance } from '../../types/swap';
21+
import { Balance, SwapResponse } from '../../types/swap';
2222
import { DEX, pTON } from '@ston-fi/sdk';
23-
import {
24-
Asset,
25-
Factory,
26-
JettonRoot,
27-
MAINNET_FACTORY_ADDR,
28-
Pool,
29-
PoolType,
30-
ReadinessStatus,
31-
Vault,
32-
VaultJetton,
33-
VaultNative,
34-
} from '@dedust/sdk';
23+
3524
import { sleep } from '@lifeomic/attempt';
3625

3726
export class Swap extends Services {
3827
/**
3928
* swap
4029
*/
41-
public async createSwap(userWalletAddress: string, bestRoute: BestRoute) {
42-
try {
43-
switch (bestRoute.selected_pool.dex) {
44-
case 'stonfi':
45-
return await this.createStonSwap(userWalletAddress, bestRoute);
46-
case 'dedust':
47-
return await this.createDedustSwap(userWalletAddress, bestRoute);
48-
49-
default:
50-
break;
51-
}
52-
} catch (error) {
53-
console.log(error);
54-
}
55-
}
56-
57-
/**
58-
* createStonSwap
59-
*
60-
*/
61-
private async createStonSwap(userWalletAddress: string, bestRoute: BestRoute) {
62-
let jettonData: Balance | undefined;
63-
let swapTxParams: SenderArguments | null = null;
64-
const isMintless = supportedMintlessTokens.includes(bestRoute.selected_pool.token0_address);
65-
if (isMintless) {
66-
jettonData = await this.client.tonapi.getJettonData(
67-
userWalletAddress,
68-
bestRoute.selected_pool.token0_address,
69-
);
70-
}
71-
72-
let customPayload: Cell | undefined;
73-
let stateInit: { code: Cell; data: Cell } | undefined;
74-
75-
if (isMintless && jettonData?.extensions?.includes('custom_payload')) {
76-
const offerJettonCustomPayload = await this.client.tonapi.getCustomPayload(
77-
userWalletAddress,
78-
bestRoute.selected_pool.token0_address,
79-
);
80-
81-
if (!offerJettonCustomPayload) {
82-
throw new Error('Unable to retrieve custom payload. Please try again.');
83-
}
84-
customPayload = Cell.fromBoc(
85-
Buffer.from(offerJettonCustomPayload.custom_payload, 'hex'),
86-
)[0];
87-
const stateInitCell = Cell.fromBoc(
88-
Buffer.from(offerJettonCustomPayload.state_init, 'hex'),
89-
)[0].beginParse();
90-
91-
stateInit = {
92-
code: stateInitCell.loadRef(),
93-
data: stateInitCell.loadRef(),
94-
};
95-
}
96-
97-
const router =
98-
bestRoute.selected_pool.router_address === STON_ROUTER_V1
99-
? this.client.tonClient.open(
100-
DEX.v1.Router.create(bestRoute.selected_pool.router_address),
101-
)
102-
: this.client.tonClient.open(
103-
DEX.v2_1.Router.create(bestRoute.selected_pool.router_address),
104-
);
105-
106-
const pTon =
107-
bestRoute.selected_pool.router_address === STON_ROUTER_V1
108-
? new pTON.v1()
109-
: new pTON.v2_1(PTON_V2);
110-
const gasConstants =
111-
bestRoute.selected_pool.router_address === STON_ROUTER_V1
112-
? DEX.v1.Router.gasConstants
113-
: DEX.v2_1.Router.gasConstants;
114-
115-
if (bestRoute.pool_data.route[0] === TON_ADDRESS) {
116-
swapTxParams = await router.getSwapTonToJettonTxParams({
117-
userWalletAddress: userWalletAddress,
118-
proxyTon: pTon,
119-
offerAmount: bestRoute.pool_data.pay,
120-
askJettonAddress: bestRoute.pool_data.route[1],
121-
minAskAmount: BigInt(bestRoute.pool_data.minimumReceive),
122-
referralAddress: address(feeWallet),
123-
});
124-
} else if (bestRoute.pool_data.route[1] === TON_ADDRESS) {
125-
swapTxParams = await router.getSwapJettonToTonTxParams({
126-
userWalletAddress: userWalletAddress,
127-
offerJettonAddress: bestRoute.pool_data.route[0],
128-
offerAmount: bestRoute.pool_data.pay,
129-
proxyTon: pTon,
130-
jettonCustomPayload: customPayload ? customPayload : undefined,
131-
minAskAmount: BigInt(bestRoute.pool_data.minimumReceive),
132-
referralAddress: feeWallet,
133-
gasAmount: isMintless
134-
? gasConstants.swapJettonToJetton.gasAmount + toNano(0.1)
135-
: undefined,
136-
});
137-
} else {
138-
swapTxParams = await router.getSwapJettonToJettonTxParams({
139-
userWalletAddress: userWalletAddress,
140-
offerJettonAddress: bestRoute.selected_pool.token0_address,
141-
offerAmount: BigInt(bestRoute.pool_data.pay),
142-
askJettonAddress: bestRoute.selected_pool.token1_address,
143-
jettonCustomPayload: customPayload ? customPayload : undefined,
144-
145-
minAskAmount: BigInt(bestRoute.pool_data.minimumReceive),
146-
referralAddress: address(feeWallet),
147-
gasAmount: stateInit
148-
? gasConstants.swapJettonToJetton.gasAmount + toNano(0.1)
149-
: undefined,
150-
});
151-
}
152-
return swapTxParams satisfies SenderArguments;
153-
}
154-
155-
private async createDedustSwap(userWalletAddress: string, bestRoute: BestRoute) {
156-
let [jetton0, jetton1, jetton2] = bestRoute.pool_data.route;
157-
158-
let Asset0: Asset;
159-
let Asset1: Asset;
160-
let Asset2: Asset;
161-
let Vault0: OpenedContract<VaultNative> | OpenedContract<VaultJetton>;
162-
let Vault2: OpenedContract<VaultNative> | OpenedContract<VaultJetton>;
163-
let PoolB: OpenedContract<Pool>;
164-
let RequestAddress: Address;
165-
let StoreAddress: Address;
166-
167-
let TxAmount = toNano('0.3');
168-
169-
let jettonData: Balance | undefined;
170-
const isMintless = supportedMintlessTokens.includes(bestRoute.selected_pool.token0_address);
171-
if (isMintless) {
172-
jettonData = await this.client.tonapi.getJettonData(
173-
userWalletAddress,
174-
bestRoute.selected_pool.token0_address,
175-
);
176-
}
177-
178-
let customPayload: Cell | undefined;
179-
let stateInit: { code: Cell; data: Cell } | undefined;
180-
if (isMintless && jettonData?.extensions?.includes('custom_payload')) {
181-
const offerJettonCustomPayload = await this.client.tonapi.getCustomPayload(
182-
userWalletAddress,
183-
bestRoute.selected_pool.token0_address,
184-
);
185-
186-
if (!offerJettonCustomPayload) {
187-
throw new Error('Unable to retrieve custom payload. Please try again.');
188-
}
189-
customPayload = Cell.fromBoc(
190-
Buffer.from(offerJettonCustomPayload.custom_payload, 'hex'),
191-
)[0];
192-
const stateInitCell = Cell.fromBoc(
193-
Buffer.from(offerJettonCustomPayload.state_init, 'hex'),
194-
)[0].beginParse();
195-
196-
stateInit = {
197-
code: stateInitCell.loadRef(),
198-
data: stateInitCell.loadRef(),
199-
};
200-
}
201-
const factory = this.client.tonClient.open(Factory.createFromAddress(MAINNET_FACTORY_ADDR));
202-
await sleep(500);
203-
if (jetton0 == 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c') {
204-
if (jetton2 && jetton2 !== '') {
205-
throw new Error('This request can not process!');
206-
}
207-
208-
Asset0 = Asset.native();
209-
Vault0 = this.client.tonClient.open(await factory.getNativeVault());
210-
} else {
211-
Asset0 = Asset.jetton(Address.parse(jetton0));
212-
Vault0 = this.client.tonClient.open(
213-
await factory.getJettonVault(Address.parse(jetton0)),
214-
);
215-
}
216-
await sleep(500);
217-
if (jetton1 == 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c') {
218-
Asset1 = Asset.native();
219-
} else {
220-
Asset1 = Asset.jetton(Address.parse(jetton1));
221-
}
222-
await sleep(500);
223-
const PoolA = this.client.tonClient.open(
224-
await factory.getPool(PoolType.VOLATILE, [Asset0, Asset1]),
225-
);
226-
if (jetton2 && jetton2 !== '') {
227-
if (jetton2 == 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c') {
228-
Asset2 = Asset.native();
229-
} else {
230-
Asset2 = Asset.jetton(Address.parse(jetton2));
231-
}
232-
await sleep(500);
233-
234-
PoolB = this.client.tonClient.open(
235-
await factory.getPool(PoolType.VOLATILE, [Asset1, Asset2]),
236-
);
237-
}
238-
239-
let PAY_LOAD: Cell;
240-
let pay: Cell;
241-
if (jetton0 == 'EQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM9c') {
242-
// @ts-expect-error because dedust doesn't let user to build custom payloads
243-
PAY_LOAD = Vault.packSwapParams({});
244-
245-
StoreAddress = PoolA.address;
246-
RequestAddress = Vault0.address;
247-
248-
TxAmount = BigInt(bestRoute.pool_data.pay) + toNano('0.1');
249-
250-
pay = beginCell()
251-
.storeUint(DEDUST_SWAP, 32)
252-
.storeUint(0, 64)
253-
.storeCoins(BigInt(bestRoute.pool_data.pay))
254-
.storeAddress(StoreAddress)
255-
.storeUint(0, 1)
256-
.storeCoins(0)
257-
.storeMaybeRef(null)
258-
.storeRef(PAY_LOAD)
259-
.endCell();
260-
} else {
261-
const JRoot = this.client.tonClient.open(
262-
JettonRoot.createFromAddress(Address.parse(jetton0)),
263-
);
264-
const JWallet = this.client.tonClient.open(
265-
await JRoot.getWallet(Address.parseRaw(userWalletAddress)),
266-
);
267-
268-
StoreAddress = Vault0.address;
269-
RequestAddress = JWallet.address;
270-
271-
if (jetton2 == '') {
272-
PAY_LOAD = VaultJetton.createSwapPayload({
273-
poolAddress: Address.parse(PoolA.address.toString()),
274-
});
275-
} else {
276-
PAY_LOAD = VaultJetton.createSwapPayload({
277-
poolAddress: Address.parse(PoolA.address.toString()),
278-
limit: BigInt(bestRoute.pool_data.innerMinimumReceive),
279-
next: {
280-
poolAddress: PoolB!.address,
281-
},
282-
});
283-
}
284-
285-
pay = beginCell()
286-
.storeUint(DEDUST_TRANSFER, 32)
287-
.storeUint(0, 64)
288-
.storeCoins(BigInt(bestRoute.pool_data.pay))
289-
.storeAddress(StoreAddress)
290-
.storeAddress(Address.parseRaw(userWalletAddress))
291-
.storeMaybeRef(customPayload ? customPayload : null)
292-
.storeCoins(toNano('0.25'))
293-
.storeMaybeRef(PAY_LOAD)
294-
.endCell();
295-
}
296-
return {
297-
to: RequestAddress,
298-
value: isMintless ? TxAmount + toNano(0.07) : TxAmount,
299-
body: pay,
300-
init: stateInit,
301-
} satisfies SenderArguments;
30+
public async createSwap(userWalletAddress: string, bestRoute: BestRoute, app_id?: string) {
31+
return await this.client.request.send<SwapResponse>({
32+
method: 'POST',
33+
headers: {
34+
...(app_id ? { app_id: app_id } : {}),
35+
},
36+
url: 'v2/routes/boc',
37+
data: {
38+
userWallet: userWalletAddress,
39+
bestRoute: bestRoute,
40+
},
41+
});
30242
}
30343
}

src/services/swap/swap.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ test(
2222
1,
2323
'stonfi',
2424
);
25+
2526
const swap = await client.swap.createSwap(userWallet, bestRoute);
2627
expect(swap).toBeObject();
2728
expect(swap?.value).not.toBeUndefined();
28-
expect(swap?.value).toBeGreaterThan(toNano(1));
29+
expect(BigInt(swap?.value)).toBeGreaterThan(toNano(1));
2930
},
3031
{ timeout: 10000 },
3132
);
@@ -46,7 +47,7 @@ test(
4647
const swap = await client.swap.createSwap(userWallet, bestRoute);
4748
expect(swap).toBeObject();
4849
expect(swap?.value).not.toBeUndefined();
49-
expect(swap?.value).toBeGreaterThan(toNano(1));
50+
expect(BigInt(swap?.value)).toBeGreaterThan(toNano(1));
5051
},
5152
{ timeout: 10000 },
5253
);

src/types/swap.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,13 @@ export interface WalletInfo {
7979
get_methods: string[];
8080
is_wallet: boolean;
8181
}
82+
83+
export interface SwapResponse {
84+
to: string;
85+
value: string;
86+
body: string;
87+
init: {
88+
code: string;
89+
data: string;
90+
};
91+
}

0 commit comments

Comments
 (0)