@mayanfinance/blink-sdk 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +15 -0
- package/dist/amount.d.ts +4 -0
- package/dist/amount.js +32 -0
- package/dist/client.d.ts +17 -0
- package/dist/client.js +32 -0
- package/dist/deposit.d.ts +64 -0
- package/dist/deposit.js +526 -0
- package/dist/evm.d.ts +237 -0
- package/dist/evm.js +679 -0
- package/dist/gen/blink/v1/admin_pb.d.ts +1338 -0
- package/dist/gen/blink/v1/admin_pb.js +234 -0
- package/dist/gen/blink/v1/api_pb.d.ts +1811 -0
- package/dist/gen/blink/v1/api_pb.js +294 -0
- package/dist/gen/blink/v1/partner_pb.d.ts +1020 -0
- package/dist/gen/blink/v1/partner_pb.js +247 -0
- package/dist/gen/blink/v1/ratelimit_pb.d.ts +10 -0
- package/dist/gen/blink/v1/ratelimit_pb.js +13 -0
- package/dist/gen/google/api/annotations_pb.d.ts +11 -0
- package/dist/gen/google/api/annotations_pb.js +14 -0
- package/dist/gen/google/api/http_pb.d.ts +111 -0
- package/dist/gen/google/api/http_pb.js +23 -0
- package/dist/hash.d.ts +7 -0
- package/dist/hash.js +76 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +13 -0
- package/dist/order.d.ts +42 -0
- package/dist/order.js +173 -0
- package/dist/sui.d.ts +74 -0
- package/dist/sui.js +310 -0
- package/dist/svm.d.ts +36 -0
- package/dist/svm.js +504 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/dist/wallets.d.ts +84 -0
- package/dist/wallets.js +409 -0
- package/package.json +62 -0
package/dist/deposit.js
ADDED
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
import { formatAmount } from "./amount.js";
|
|
2
|
+
import { buildEvmDepositCall, detectPermit, encodeApprove, evmAllowance, evmAllowanceSlot, evmEstimateGas, evmGasPrice, permit2WitnessDepositTypedData, permit2WitnessSwapDepositTypedData, evmBalance, isNativeEvmToken, PERMIT2_ADDRESS, permit2TypedData, permitDeadline, permitTypedData, randomPermit2Nonce, splitSignature, usesDirectTransfer, waitForEvmTransaction, } from "./evm.js";
|
|
3
|
+
import { assertQuoteHash } from "./hash.js";
|
|
4
|
+
import { assertDepositDeadline, isSourceSwap } from "./order.js";
|
|
5
|
+
import { SUI_COIN_TYPE, buildSuiDeposit, buildSuiSwapDeposit, sponsorSuiTransaction, suiBalance, } from "./sui.js";
|
|
6
|
+
import { buildSvmDeposit, buildSvmSwapDeposit, svmBalance } from "./svm.js";
|
|
7
|
+
import { sendEvmCalls, sendEvmTransaction, signAndSendSuiTransaction, signAndSendSvmTransaction, signSuiTransaction, signSvmTransaction, signEvmTypedData, supportsAtomicBatch, switchEvmChain, } from "./wallets.js";
|
|
8
|
+
const trackGaslessSubmission = async (client, quoteHash) => {
|
|
9
|
+
for (;;) {
|
|
10
|
+
const submission = await client.blink.submitGaslessOrder({ quoteHash });
|
|
11
|
+
if (submission.status === "failed" || submission.status === "rejected") {
|
|
12
|
+
throw new Error(submission.error ?? "gasless submission failed");
|
|
13
|
+
}
|
|
14
|
+
if (submission.txHash) {
|
|
15
|
+
return submission.txHash;
|
|
16
|
+
}
|
|
17
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const rpcUrlOf = (chain) => {
|
|
21
|
+
if (!chain.rpcUrl) {
|
|
22
|
+
throw new Error(`no public rpc configured for ${chain.chain}`);
|
|
23
|
+
}
|
|
24
|
+
return chain.rpcUrl;
|
|
25
|
+
};
|
|
26
|
+
const NATIVE_EVM_TOKEN = "0x0000000000000000000000000000000000000000";
|
|
27
|
+
const SVM_MIN_FEE_LAMPORTS = 2000000n;
|
|
28
|
+
const SUI_MIN_FEE_MIST = 20000000n;
|
|
29
|
+
export class InsufficientGasError extends Error {
|
|
30
|
+
required;
|
|
31
|
+
balance;
|
|
32
|
+
constructor(symbol, decimals, required, balance) {
|
|
33
|
+
super(`not enough ${symbol} for network fees: need ${formatAmount(required, decimals)}, have ${formatAmount(balance, decimals)}`);
|
|
34
|
+
this.required = required;
|
|
35
|
+
this.balance = balance;
|
|
36
|
+
this.name = "InsufficientGasError";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export class EvmChainAdapter {
|
|
40
|
+
client;
|
|
41
|
+
chain;
|
|
42
|
+
chainId;
|
|
43
|
+
kind = "evm";
|
|
44
|
+
constructor(client, chain, chainId) {
|
|
45
|
+
this.client = client;
|
|
46
|
+
this.chain = chain;
|
|
47
|
+
this.chainId = chainId;
|
|
48
|
+
}
|
|
49
|
+
balance(owner, token) {
|
|
50
|
+
return evmBalance(rpcUrlOf(this.chain), token, owner);
|
|
51
|
+
}
|
|
52
|
+
async canPayGas(owner) {
|
|
53
|
+
const balance = await evmBalance(rpcUrlOf(this.chain), NATIVE_EVM_TOKEN, owner);
|
|
54
|
+
return balance > 0n;
|
|
55
|
+
}
|
|
56
|
+
async gasWithAllowance(owner, call, token, spender, amount) {
|
|
57
|
+
const rpcUrl = rpcUrlOf(this.chain);
|
|
58
|
+
try {
|
|
59
|
+
const slot = await evmAllowanceSlot(rpcUrl, token, owner, spender);
|
|
60
|
+
return slot && (await evmEstimateGas(rpcUrl, owner, call, { token, slot, amount }));
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async assertCanPay(owner, required) {
|
|
67
|
+
if (required === undefined) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const balance = await evmBalance(rpcUrlOf(this.chain), NATIVE_EVM_TOKEN, owner);
|
|
71
|
+
if (balance < required) {
|
|
72
|
+
throw new InsufficientGasError(this.chain.nativeSymbol, this.chain.nativeDecimals, required, balance);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async steps(wallet, quoteHash, params, options) {
|
|
76
|
+
if (wallet.kind !== "evm") {
|
|
77
|
+
throw new Error(`${this.chain.chain} needs an evm wallet`);
|
|
78
|
+
}
|
|
79
|
+
assertQuoteHash(quoteHash, params);
|
|
80
|
+
assertDepositDeadline(options?.depositDeadline);
|
|
81
|
+
const rpcUrl = rpcUrlOf(this.chain);
|
|
82
|
+
const amountIn = BigInt(params.amountIn);
|
|
83
|
+
const native = isNativeEvmToken(params.srcToken);
|
|
84
|
+
const gasPrice = await evmGasPrice(rpcUrl);
|
|
85
|
+
const feeOf = async (call) => gasPrice * (await evmEstimateGas(rpcUrl, wallet.address, call));
|
|
86
|
+
const selectChain = () => switchEvmChain(wallet.provider, this.chainId, this.chain.chain, this.chain.nativeSymbol, rpcUrl);
|
|
87
|
+
const send = async (call) => {
|
|
88
|
+
await selectChain();
|
|
89
|
+
return sendEvmTransaction(wallet.provider, {
|
|
90
|
+
from: wallet.address,
|
|
91
|
+
...call,
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
const base = {
|
|
95
|
+
quoteHash,
|
|
96
|
+
params,
|
|
97
|
+
directTransfer: options?.directTransfer,
|
|
98
|
+
};
|
|
99
|
+
if (usesDirectTransfer(base)) {
|
|
100
|
+
const call = buildEvmDepositCall(base);
|
|
101
|
+
const fee = await feeOf(call);
|
|
102
|
+
await this.assertCanPay(wallet.address, fee);
|
|
103
|
+
return [{ kind: "deposit", fee, run: () => send(call) }];
|
|
104
|
+
}
|
|
105
|
+
const request = {
|
|
106
|
+
...base,
|
|
107
|
+
swap: isSourceSwap(params)
|
|
108
|
+
? await this.client.blink.getSwapInstructions({ quoteHash })
|
|
109
|
+
: undefined,
|
|
110
|
+
router: this.chain.router,
|
|
111
|
+
};
|
|
112
|
+
const call = buildEvmDepositCall(request);
|
|
113
|
+
const target = call.to;
|
|
114
|
+
const permitTarget = request.swap ? target : this.chain.router;
|
|
115
|
+
const deposit = { kind: "deposit", run: () => send(call) };
|
|
116
|
+
if (native) {
|
|
117
|
+
deposit.fee = await feeOf(call);
|
|
118
|
+
await this.assertCanPay(wallet.address, deposit.fee + BigInt(call.value));
|
|
119
|
+
return [deposit];
|
|
120
|
+
}
|
|
121
|
+
const allowance = await evmAllowance(rpcUrl, params.srcToken, wallet.address, target);
|
|
122
|
+
if (allowance >= amountIn) {
|
|
123
|
+
deposit.fee = await feeOf(call);
|
|
124
|
+
await this.assertCanPay(wallet.address, deposit.fee);
|
|
125
|
+
return [deposit];
|
|
126
|
+
}
|
|
127
|
+
const depositGas = await this.gasWithAllowance(wallet.address, call, params.srcToken, target, amountIn);
|
|
128
|
+
deposit.fee = depositGas === undefined ? undefined : depositGas * gasPrice;
|
|
129
|
+
const permit = permitTarget
|
|
130
|
+
? await detectPermit(rpcUrl, params.srcToken, wallet.address, this.chainId)
|
|
131
|
+
: undefined;
|
|
132
|
+
if (permitTarget && permit) {
|
|
133
|
+
await this.assertCanPay(wallet.address, deposit.fee);
|
|
134
|
+
return [
|
|
135
|
+
{
|
|
136
|
+
kind: "deposit",
|
|
137
|
+
fee: deposit.fee,
|
|
138
|
+
run: async () => {
|
|
139
|
+
await selectChain();
|
|
140
|
+
const deadline = permitDeadline();
|
|
141
|
+
const signature = splitSignature(await signEvmTypedData(wallet.provider, wallet.address, permitTypedData(permit, this.chainId, params.srcToken, wallet.address, permitTarget, amountIn, deadline)));
|
|
142
|
+
return send(buildEvmDepositCall(request, { kind: "permit", deadline, signature }));
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
];
|
|
146
|
+
}
|
|
147
|
+
const permit2Allowance = permitTarget
|
|
148
|
+
? await evmAllowance(rpcUrl, params.srcToken, wallet.address, PERMIT2_ADDRESS)
|
|
149
|
+
: 0n;
|
|
150
|
+
if (permitTarget && permit2Allowance >= amountIn) {
|
|
151
|
+
await this.assertCanPay(wallet.address, deposit.fee);
|
|
152
|
+
return [
|
|
153
|
+
{
|
|
154
|
+
kind: "deposit",
|
|
155
|
+
fee: deposit.fee,
|
|
156
|
+
run: async () => {
|
|
157
|
+
await selectChain();
|
|
158
|
+
const nonce = randomPermit2Nonce();
|
|
159
|
+
const deadline = permitDeadline();
|
|
160
|
+
const signature = await signEvmTypedData(wallet.provider, wallet.address, permit2TypedData(this.chainId, params.srcToken, amountIn, permitTarget, nonce, deadline));
|
|
161
|
+
return send(buildEvmDepositCall(request, {
|
|
162
|
+
kind: "permit2",
|
|
163
|
+
nonce,
|
|
164
|
+
deadline,
|
|
165
|
+
signature,
|
|
166
|
+
}));
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
];
|
|
170
|
+
}
|
|
171
|
+
const approveCall = {
|
|
172
|
+
to: params.srcToken,
|
|
173
|
+
data: encodeApprove(target, amountIn),
|
|
174
|
+
};
|
|
175
|
+
const approveFee = await feeOf(approveCall);
|
|
176
|
+
const totalFee = deposit.fee === undefined ? undefined : approveFee + deposit.fee;
|
|
177
|
+
await this.assertCanPay(wallet.address, totalFee ?? approveFee);
|
|
178
|
+
if (await supportsAtomicBatch(wallet.provider, wallet.address, this.chainId)) {
|
|
179
|
+
return [
|
|
180
|
+
{
|
|
181
|
+
kind: "deposit",
|
|
182
|
+
fee: totalFee,
|
|
183
|
+
run: async () => {
|
|
184
|
+
await selectChain();
|
|
185
|
+
return sendEvmCalls(wallet.provider, wallet.address, this.chainId, [
|
|
186
|
+
approveCall,
|
|
187
|
+
call,
|
|
188
|
+
]);
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
];
|
|
192
|
+
}
|
|
193
|
+
const approve = {
|
|
194
|
+
kind: "approve",
|
|
195
|
+
fee: approveFee,
|
|
196
|
+
run: async () => {
|
|
197
|
+
await selectChain();
|
|
198
|
+
const hash = await sendEvmTransaction(wallet.provider, {
|
|
199
|
+
from: wallet.address,
|
|
200
|
+
...approveCall,
|
|
201
|
+
});
|
|
202
|
+
await waitForEvmTransaction(rpcUrl, hash);
|
|
203
|
+
return hash;
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
return [approve, deposit];
|
|
207
|
+
}
|
|
208
|
+
async gaslessSteps(wallet, quoteHash, params, quote) {
|
|
209
|
+
if (wallet.kind !== "evm") {
|
|
210
|
+
throw new Error(`${this.chain.chain} needs an evm wallet`);
|
|
211
|
+
}
|
|
212
|
+
assertQuoteHash(quoteHash, params);
|
|
213
|
+
assertDepositDeadline(quote.depositDeadline);
|
|
214
|
+
const spender = this.chain.router;
|
|
215
|
+
if (!spender) {
|
|
216
|
+
throw new Error(`no router configured for ${this.chain.chain}`);
|
|
217
|
+
}
|
|
218
|
+
const rpcUrl = rpcUrlOf(this.chain);
|
|
219
|
+
const amountIn = BigInt(params.amountIn);
|
|
220
|
+
if (isNativeEvmToken(params.srcToken)) {
|
|
221
|
+
throw new Error("gasless deposits require an erc20 input token");
|
|
222
|
+
}
|
|
223
|
+
const steps = [];
|
|
224
|
+
const permit2Allowance = await evmAllowance(rpcUrl, params.srcToken, wallet.address, PERMIT2_ADDRESS);
|
|
225
|
+
const permit = permit2Allowance < amountIn
|
|
226
|
+
? await detectPermit(rpcUrl, params.srcToken, wallet.address, this.chainId)
|
|
227
|
+
: undefined;
|
|
228
|
+
if (permit2Allowance < amountIn && !permit) {
|
|
229
|
+
const approveCall = {
|
|
230
|
+
to: params.srcToken,
|
|
231
|
+
data: encodeApprove(PERMIT2_ADDRESS, (1n << 256n) - 1n),
|
|
232
|
+
};
|
|
233
|
+
const fee = (await evmGasPrice(rpcUrl)) *
|
|
234
|
+
(await evmEstimateGas(rpcUrl, wallet.address, approveCall));
|
|
235
|
+
await this.assertCanPay(wallet.address, fee);
|
|
236
|
+
steps.push({
|
|
237
|
+
kind: "approve",
|
|
238
|
+
fee,
|
|
239
|
+
run: async () => {
|
|
240
|
+
await switchEvmChain(wallet.provider, this.chainId, this.chain.chain, this.chain.nativeSymbol, rpcUrl);
|
|
241
|
+
const hash = await sendEvmTransaction(wallet.provider, {
|
|
242
|
+
from: wallet.address,
|
|
243
|
+
...approveCall,
|
|
244
|
+
});
|
|
245
|
+
await waitForEvmTransaction(rpcUrl, hash);
|
|
246
|
+
return hash;
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
const swap = isSourceSwap(params);
|
|
251
|
+
steps.push({
|
|
252
|
+
kind: "deposit",
|
|
253
|
+
run: async () => {
|
|
254
|
+
await switchEvmChain(wallet.provider, this.chainId, this.chain.chain, this.chain.nativeSymbol, rpcUrl);
|
|
255
|
+
const nonce = randomPermit2Nonce();
|
|
256
|
+
const deadline = permitDeadline();
|
|
257
|
+
const permitSignature = permit
|
|
258
|
+
? await signEvmTypedData(wallet.provider, wallet.address, permitTypedData(permit, this.chainId, params.srcToken, wallet.address, PERMIT2_ADDRESS, (1n << 256n) - 1n, deadline))
|
|
259
|
+
: undefined;
|
|
260
|
+
const typedData = swap
|
|
261
|
+
? permit2WitnessSwapDepositTypedData(this.chainId, params.srcToken, amountIn, spender, nonce, deadline, quoteHash, params.bridgeTokenSrc, BigInt(params.minBridgeOut))
|
|
262
|
+
: permit2WitnessDepositTypedData(this.chainId, params.srcToken, amountIn, spender, nonce, deadline, quoteHash);
|
|
263
|
+
const signature = await signEvmTypedData(wallet.provider, wallet.address, typedData);
|
|
264
|
+
await this.client.blink.submitGaslessOrder({
|
|
265
|
+
quoteHash,
|
|
266
|
+
order: {
|
|
267
|
+
case: "evm",
|
|
268
|
+
value: {
|
|
269
|
+
owner: wallet.address,
|
|
270
|
+
permit2Nonce: nonce.toString(),
|
|
271
|
+
deadline,
|
|
272
|
+
signature,
|
|
273
|
+
permitSignature,
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
return trackGaslessSubmission(this.client, quoteHash);
|
|
278
|
+
},
|
|
279
|
+
});
|
|
280
|
+
return steps;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
export class SvmChainAdapter {
|
|
284
|
+
chain;
|
|
285
|
+
client;
|
|
286
|
+
kind = "svm";
|
|
287
|
+
constructor(chain, client) {
|
|
288
|
+
this.chain = chain;
|
|
289
|
+
this.client = client;
|
|
290
|
+
}
|
|
291
|
+
balance(owner, token) {
|
|
292
|
+
return svmBalance(rpcUrlOf(this.chain), token, owner);
|
|
293
|
+
}
|
|
294
|
+
async canPayGas(owner) {
|
|
295
|
+
const balance = await svmBalance(rpcUrlOf(this.chain), "11111111111111111111111111111111", owner);
|
|
296
|
+
return balance >= SVM_MIN_FEE_LAMPORTS;
|
|
297
|
+
}
|
|
298
|
+
async steps(wallet, quoteHash, params, options) {
|
|
299
|
+
if (wallet.kind !== "svm") {
|
|
300
|
+
throw new Error("svm needs an svm wallet");
|
|
301
|
+
}
|
|
302
|
+
assertQuoteHash(quoteHash, params);
|
|
303
|
+
assertDepositDeadline(options?.depositDeadline);
|
|
304
|
+
if (!this.chain.treasury) {
|
|
305
|
+
throw new Error("svm deposits are unavailable");
|
|
306
|
+
}
|
|
307
|
+
const rpcUrl = rpcUrlOf(this.chain);
|
|
308
|
+
if (isSourceSwap(params)) {
|
|
309
|
+
const client = this.client;
|
|
310
|
+
if (!client) {
|
|
311
|
+
throw new Error("svm swap deposits need an api client");
|
|
312
|
+
}
|
|
313
|
+
return [
|
|
314
|
+
{
|
|
315
|
+
kind: "deposit",
|
|
316
|
+
run: async () => {
|
|
317
|
+
const instructions = await client.blink.getSwapInstructions({
|
|
318
|
+
quoteHash,
|
|
319
|
+
});
|
|
320
|
+
if (instructions.payload.case !== "svm") {
|
|
321
|
+
throw new Error("expected svm swap instructions");
|
|
322
|
+
}
|
|
323
|
+
const transaction = await buildSvmSwapDeposit({
|
|
324
|
+
rpcUrl,
|
|
325
|
+
chain: this.chain.chain,
|
|
326
|
+
programId: instructions.routerAddress,
|
|
327
|
+
owner: wallet.address,
|
|
328
|
+
srcToken: params.srcToken,
|
|
329
|
+
bridgeToken: params.bridgeTokenSrc,
|
|
330
|
+
amount: BigInt(params.amountIn),
|
|
331
|
+
minBridgeOut: BigInt(params.minBridgeOut),
|
|
332
|
+
quoteHash,
|
|
333
|
+
swap: instructions.payload.value,
|
|
334
|
+
swapProgramSigner: this.chain.treasury,
|
|
335
|
+
});
|
|
336
|
+
return signAndSendSvmTransaction(wallet.wallet, wallet.address, transaction);
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
];
|
|
340
|
+
}
|
|
341
|
+
return [
|
|
342
|
+
{
|
|
343
|
+
kind: "deposit",
|
|
344
|
+
run: async () => {
|
|
345
|
+
const transaction = await buildSvmDeposit({
|
|
346
|
+
rpcUrl,
|
|
347
|
+
programId: params.routerAddress,
|
|
348
|
+
owner: wallet.address,
|
|
349
|
+
token: params.srcToken,
|
|
350
|
+
amount: BigInt(params.amountIn),
|
|
351
|
+
quoteHash,
|
|
352
|
+
});
|
|
353
|
+
return signAndSendSvmTransaction(wallet.wallet, wallet.address, transaction);
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
];
|
|
357
|
+
}
|
|
358
|
+
async gaslessSteps(wallet, quoteHash, params, quote) {
|
|
359
|
+
if (wallet.kind !== "svm") {
|
|
360
|
+
throw new Error("svm needs an svm wallet");
|
|
361
|
+
}
|
|
362
|
+
assertQuoteHash(quoteHash, params);
|
|
363
|
+
assertDepositDeadline(quote.depositDeadline);
|
|
364
|
+
if (isSourceSwap(params)) {
|
|
365
|
+
throw new Error("gasless swap deposits are unavailable on svm");
|
|
366
|
+
}
|
|
367
|
+
if (!this.chain.treasury) {
|
|
368
|
+
throw new Error("svm deposits are unavailable");
|
|
369
|
+
}
|
|
370
|
+
const sponsor = quote.sponsorAddress;
|
|
371
|
+
if (!sponsor) {
|
|
372
|
+
throw new Error("gasless deposits are unavailable on svm");
|
|
373
|
+
}
|
|
374
|
+
if (!this.client) {
|
|
375
|
+
throw new Error("gasless deposits need an api client");
|
|
376
|
+
}
|
|
377
|
+
const client = this.client;
|
|
378
|
+
const rpcUrl = rpcUrlOf(this.chain);
|
|
379
|
+
return [
|
|
380
|
+
{
|
|
381
|
+
kind: "deposit",
|
|
382
|
+
run: async () => {
|
|
383
|
+
const transaction = await buildSvmDeposit({
|
|
384
|
+
rpcUrl,
|
|
385
|
+
programId: params.routerAddress,
|
|
386
|
+
owner: wallet.address,
|
|
387
|
+
token: params.srcToken,
|
|
388
|
+
amount: BigInt(params.amountIn),
|
|
389
|
+
quoteHash,
|
|
390
|
+
feePayer: sponsor,
|
|
391
|
+
});
|
|
392
|
+
const signed = await signSvmTransaction(wallet.wallet, wallet.address, transaction);
|
|
393
|
+
await client.blink.submitGaslessOrder({
|
|
394
|
+
quoteHash,
|
|
395
|
+
order: {
|
|
396
|
+
case: "svm",
|
|
397
|
+
value: { txBase64: btoa(String.fromCharCode(...signed)) },
|
|
398
|
+
},
|
|
399
|
+
});
|
|
400
|
+
return trackGaslessSubmission(client, quoteHash);
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
];
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
export class SuiChainAdapter {
|
|
407
|
+
chain;
|
|
408
|
+
client;
|
|
409
|
+
kind = "sui";
|
|
410
|
+
constructor(chain, client) {
|
|
411
|
+
this.chain = chain;
|
|
412
|
+
this.client = client;
|
|
413
|
+
}
|
|
414
|
+
balance(owner, token) {
|
|
415
|
+
return suiBalance(rpcUrlOf(this.chain), token, owner);
|
|
416
|
+
}
|
|
417
|
+
async canPayGas(owner) {
|
|
418
|
+
const balance = await suiBalance(rpcUrlOf(this.chain), SUI_COIN_TYPE, owner);
|
|
419
|
+
return balance >= SUI_MIN_FEE_MIST;
|
|
420
|
+
}
|
|
421
|
+
async steps(wallet, quoteHash, params, options) {
|
|
422
|
+
if (wallet.kind !== "sui") {
|
|
423
|
+
throw new Error("sui needs a sui wallet");
|
|
424
|
+
}
|
|
425
|
+
assertQuoteHash(quoteHash, params);
|
|
426
|
+
assertDepositDeadline(options?.depositDeadline);
|
|
427
|
+
const escrow = this.chain.escrow;
|
|
428
|
+
if (!escrow) {
|
|
429
|
+
throw new Error("sui deposits are unavailable");
|
|
430
|
+
}
|
|
431
|
+
if (isSourceSwap(params) && !this.client) {
|
|
432
|
+
throw new Error("sui swap deposits need an api client");
|
|
433
|
+
}
|
|
434
|
+
const client = this.client;
|
|
435
|
+
const rpcUrl = rpcUrlOf(this.chain);
|
|
436
|
+
return [
|
|
437
|
+
{
|
|
438
|
+
kind: "deposit",
|
|
439
|
+
run: async () => signAndSendSuiTransaction(wallet.wallet, wallet.address, await suiDepositTransaction(client, rpcUrl, escrow, wallet.address, quoteHash, params)),
|
|
440
|
+
},
|
|
441
|
+
];
|
|
442
|
+
}
|
|
443
|
+
async gaslessSteps(wallet, quoteHash, params, quote) {
|
|
444
|
+
if (wallet.kind !== "sui") {
|
|
445
|
+
throw new Error("sui needs a sui wallet");
|
|
446
|
+
}
|
|
447
|
+
assertQuoteHash(quoteHash, params);
|
|
448
|
+
assertDepositDeadline(quote.depositDeadline);
|
|
449
|
+
const escrow = this.chain.escrow;
|
|
450
|
+
if (!escrow) {
|
|
451
|
+
throw new Error("sui deposits are unavailable");
|
|
452
|
+
}
|
|
453
|
+
const sponsor = quote.sponsorAddress;
|
|
454
|
+
const gasBudget = quote.sponsorGasBudget;
|
|
455
|
+
if (!sponsor || gasBudget === undefined) {
|
|
456
|
+
throw new Error("gasless deposits are unavailable on sui");
|
|
457
|
+
}
|
|
458
|
+
if (!this.client) {
|
|
459
|
+
throw new Error("gasless deposits need an api client");
|
|
460
|
+
}
|
|
461
|
+
const client = this.client;
|
|
462
|
+
const rpcUrl = rpcUrlOf(this.chain);
|
|
463
|
+
return [
|
|
464
|
+
{
|
|
465
|
+
kind: "deposit",
|
|
466
|
+
run: async () => {
|
|
467
|
+
const transaction = await sponsorSuiTransaction(rpcUrl, await suiDepositTransaction(client, rpcUrl, escrow, wallet.address, quoteHash, params), sponsor, gasBudget);
|
|
468
|
+
const signed = await signSuiTransaction(wallet.wallet, wallet.address, transaction);
|
|
469
|
+
await client.blink.submitGaslessOrder({
|
|
470
|
+
quoteHash,
|
|
471
|
+
order: {
|
|
472
|
+
case: "sui",
|
|
473
|
+
value: { txBase64: signed.bytes, signature: signed.signature },
|
|
474
|
+
},
|
|
475
|
+
});
|
|
476
|
+
return trackGaslessSubmission(client, quoteHash);
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
];
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
const suiDepositTransaction = async (client, rpcUrl, escrow, owner, quoteHash, params) => {
|
|
483
|
+
if (!isSourceSwap(params)) {
|
|
484
|
+
return buildSuiDeposit({
|
|
485
|
+
rpcUrl,
|
|
486
|
+
packageId: params.routerAddress,
|
|
487
|
+
escrowObject: escrow,
|
|
488
|
+
owner,
|
|
489
|
+
coinType: params.srcToken,
|
|
490
|
+
amount: BigInt(params.amountIn),
|
|
491
|
+
quoteHash,
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
if (!client) {
|
|
495
|
+
throw new Error("sui swap deposits need an api client");
|
|
496
|
+
}
|
|
497
|
+
const instructions = await client.blink.getSwapInstructions({ quoteHash });
|
|
498
|
+
if (instructions.payload.case !== "sui") {
|
|
499
|
+
throw new Error("expected sui swap instructions");
|
|
500
|
+
}
|
|
501
|
+
return buildSuiSwapDeposit({
|
|
502
|
+
rpcUrl,
|
|
503
|
+
packageId: params.routerAddress,
|
|
504
|
+
escrowObject: escrow,
|
|
505
|
+
owner,
|
|
506
|
+
srcToken: params.srcToken,
|
|
507
|
+
bridgeToken: params.bridgeTokenSrc,
|
|
508
|
+
amountIn: BigInt(params.amountIn),
|
|
509
|
+
minBridgeOut: BigInt(params.minBridgeOut),
|
|
510
|
+
quoteHash,
|
|
511
|
+
swap: instructions.payload.value,
|
|
512
|
+
});
|
|
513
|
+
};
|
|
514
|
+
export const createChainAdapter = (client, chain, chainId) => {
|
|
515
|
+
if (chain.vm === "svm") {
|
|
516
|
+
return new SvmChainAdapter(chain, client);
|
|
517
|
+
}
|
|
518
|
+
if (chain.vm === "sui") {
|
|
519
|
+
return new SuiChainAdapter(chain, client);
|
|
520
|
+
}
|
|
521
|
+
if (chainId === undefined) {
|
|
522
|
+
throw new Error(`unknown chain id for ${chain.chain}`);
|
|
523
|
+
}
|
|
524
|
+
return new EvmChainAdapter(client, chain, chainId);
|
|
525
|
+
};
|
|
526
|
+
export const walletKindOf = (chain) => chain?.vm === "svm" ? "svm" : chain?.vm === "sui" ? "sui" : "evm";
|