@7kprotocol/sdk-ts 3.5.1 → 3.5.2-beta.0
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/lib/cjs/config/index.js +9 -0
- package/lib/cjs/features/metaAg/common.js +117 -0
- package/lib/cjs/features/metaAg/index.js +88 -93
- package/lib/cjs/features/metaAg/providers/astro.js +42 -0
- package/lib/cjs/features/metaAg/providers/bluefin.js +6 -6
- package/lib/cjs/features/metaAg/providers/bluefinx.js +87 -0
- package/lib/cjs/features/metaAg/providers/cetus.js +4 -4
- package/lib/cjs/features/metaAg/providers/flowx.js +4 -4
- package/lib/cjs/features/metaAg/providers/okx.js +181 -0
- package/lib/cjs/features/swap/buildTx.js +3 -0
- package/lib/cjs/features/swap/buildTxV2.js +10 -6
- package/lib/cjs/features/swap/config.js +2 -1
- package/lib/cjs/features/swap/getQuote.js +3 -2
- package/lib/cjs/libs/protocols/bluefinx/client.js +1 -1
- package/lib/cjs/types/config/index.d.ts +4 -0
- package/lib/cjs/types/config/index.d.ts.map +1 -1
- package/lib/cjs/types/features/metaAg/common.d.ts +33 -0
- package/lib/cjs/types/features/metaAg/common.d.ts.map +1 -0
- package/lib/cjs/types/features/metaAg/index.d.ts +11 -2
- package/lib/cjs/types/features/metaAg/index.d.ts.map +1 -1
- package/lib/cjs/types/features/metaAg/providers/astro.d.ts +9 -0
- package/lib/cjs/types/features/metaAg/providers/astro.d.ts.map +1 -0
- package/lib/cjs/types/features/metaAg/providers/bluefin.d.ts +3 -3
- package/lib/cjs/types/features/metaAg/providers/bluefin.d.ts.map +1 -1
- package/lib/cjs/types/features/metaAg/providers/bluefinx.d.ts +10 -0
- package/lib/cjs/types/features/metaAg/providers/bluefinx.d.ts.map +1 -0
- package/lib/cjs/types/features/metaAg/providers/cetus.d.ts +4 -5
- package/lib/cjs/types/features/metaAg/providers/cetus.d.ts.map +1 -1
- package/lib/cjs/types/features/metaAg/providers/flowx.d.ts +3 -3
- package/lib/cjs/types/features/metaAg/providers/flowx.d.ts.map +1 -1
- package/lib/cjs/types/features/metaAg/providers/okx.d.ts +21 -0
- package/lib/cjs/types/features/metaAg/providers/okx.d.ts.map +1 -0
- package/lib/cjs/types/features/swap/buildTx.d.ts.map +1 -1
- package/lib/cjs/types/features/swap/buildTxV2.d.ts +3 -3
- package/lib/cjs/types/features/swap/buildTxV2.d.ts.map +1 -1
- package/lib/cjs/types/features/swap/config.d.ts.map +1 -1
- package/lib/cjs/types/features/swap/getQuote.d.ts +1 -3
- package/lib/cjs/types/features/swap/getQuote.d.ts.map +1 -1
- package/lib/cjs/types/index.d.ts +3 -1
- package/lib/cjs/types/index.d.ts.map +1 -1
- package/lib/cjs/types/metaAg.js +11 -4
- package/lib/cjs/types/okx.js +6 -0
- package/lib/cjs/types/types/metaAg.d.ts +60 -8
- package/lib/cjs/types/types/metaAg.d.ts.map +1 -1
- package/lib/cjs/types/types/okx.d.ts +193 -0
- package/lib/cjs/types/types/okx.d.ts.map +1 -0
- package/lib/cjs/types/utils/condition.d.ts +1 -1
- package/lib/cjs/types/utils/condition.d.ts.map +1 -1
- package/lib/esm/config/index.js +9 -0
- package/lib/esm/features/metaAg/common.js +109 -0
- package/lib/esm/features/metaAg/index.js +89 -94
- package/lib/esm/features/metaAg/providers/astro.js +39 -0
- package/lib/esm/features/metaAg/providers/bluefin.js +6 -6
- package/lib/esm/features/metaAg/providers/bluefinx.js +84 -0
- package/lib/esm/features/metaAg/providers/cetus.js +4 -4
- package/lib/esm/features/metaAg/providers/flowx.js +4 -4
- package/lib/esm/features/metaAg/providers/okx.js +146 -0
- package/lib/esm/features/swap/buildTx.js +3 -0
- package/lib/esm/features/swap/buildTxV2.js +6 -2
- package/lib/esm/features/swap/config.js +2 -1
- package/lib/esm/features/swap/getQuote.js +3 -2
- package/lib/esm/libs/protocols/bluefinx/client.js +1 -1
- package/lib/esm/types/config/index.d.ts +4 -0
- package/lib/esm/types/config/index.d.ts.map +1 -1
- package/lib/esm/types/features/metaAg/common.d.ts +33 -0
- package/lib/esm/types/features/metaAg/common.d.ts.map +1 -0
- package/lib/esm/types/features/metaAg/index.d.ts +11 -2
- package/lib/esm/types/features/metaAg/index.d.ts.map +1 -1
- package/lib/esm/types/features/metaAg/providers/astro.d.ts +9 -0
- package/lib/esm/types/features/metaAg/providers/astro.d.ts.map +1 -0
- package/lib/esm/types/features/metaAg/providers/bluefin.d.ts +3 -3
- package/lib/esm/types/features/metaAg/providers/bluefin.d.ts.map +1 -1
- package/lib/esm/types/features/metaAg/providers/bluefinx.d.ts +10 -0
- package/lib/esm/types/features/metaAg/providers/bluefinx.d.ts.map +1 -0
- package/lib/esm/types/features/metaAg/providers/cetus.d.ts +4 -5
- package/lib/esm/types/features/metaAg/providers/cetus.d.ts.map +1 -1
- package/lib/esm/types/features/metaAg/providers/flowx.d.ts +3 -3
- package/lib/esm/types/features/metaAg/providers/flowx.d.ts.map +1 -1
- package/lib/esm/types/features/metaAg/providers/okx.d.ts +21 -0
- package/lib/esm/types/features/metaAg/providers/okx.d.ts.map +1 -0
- package/lib/esm/types/features/swap/buildTx.d.ts.map +1 -1
- package/lib/esm/types/features/swap/buildTxV2.d.ts +3 -3
- package/lib/esm/types/features/swap/buildTxV2.d.ts.map +1 -1
- package/lib/esm/types/features/swap/config.d.ts.map +1 -1
- package/lib/esm/types/features/swap/getQuote.d.ts +1 -3
- package/lib/esm/types/features/swap/getQuote.d.ts.map +1 -1
- package/lib/esm/types/index.d.ts +3 -1
- package/lib/esm/types/index.d.ts.map +1 -1
- package/lib/esm/types/metaAg.js +8 -2
- package/lib/esm/types/okx.js +5 -0
- package/lib/esm/types/types/metaAg.d.ts +60 -8
- package/lib/esm/types/types/metaAg.d.ts.map +1 -1
- package/lib/esm/types/types/okx.d.ts +193 -0
- package/lib/esm/types/types/okx.d.ts.map +1 -0
- package/lib/esm/types/utils/condition.d.ts +1 -1
- package/lib/esm/types/utils/condition.d.ts.map +1 -1
- package/package.json +3 -2
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
import { getFullnodeUrl, SuiClient } from "@mysten/sui/client";
|
|
1
|
+
import { getFullnodeUrl, SuiClient, } from "@mysten/sui/client";
|
|
2
2
|
import { coinWithBalance, Transaction, } from "@mysten/sui/transactions";
|
|
3
|
-
import { normalizeStructTag } from "@mysten/sui/utils";
|
|
4
|
-
import { _7K_META_CONFIG, _7K_META_PACKAGE_ID, _7K_META_PUBLISHED_AT, _7K_META_VAULT, } from "../../constants/_7k";
|
|
3
|
+
import { normalizeStructTag, toBase64 } from "@mysten/sui/utils";
|
|
5
4
|
import { SUI_ADDRESS_ZERO } from "../../constants/sui";
|
|
6
|
-
import { EProvider, } from "../../types/metaAg";
|
|
5
|
+
import { EProvider, isAggregatorProvider, isSwapAPIProvider, } from "../../types/metaAg";
|
|
7
6
|
import { assert } from "../../utils/condition";
|
|
8
7
|
import { SuiClientUtils } from "../../utils/SuiClientUtils";
|
|
9
8
|
import { getExpectedReturn } from "../swap/buildTx";
|
|
9
|
+
import { metaSettle, simulateAggregator, simulateBluefinX, timeout, } from "./common";
|
|
10
10
|
import { BluefinProvider } from "./providers/bluefin";
|
|
11
|
+
import { BluefinXProvider } from "./providers/bluefinx";
|
|
12
|
+
import { OkxProvider, simulateOKXSwap } from "./providers/okx";
|
|
11
13
|
const HERMES_API = "https://hermes.pyth.network";
|
|
12
14
|
const DEFAULT_PROVIDERS = {
|
|
13
15
|
[EProvider.BLUEFIN7K]: {},
|
|
14
16
|
[EProvider.FLOWX]: {},
|
|
15
17
|
[EProvider.CETUS]: {},
|
|
18
|
+
[EProvider.ASTRO]: {},
|
|
19
|
+
[EProvider.BLUEFINX]: {},
|
|
16
20
|
};
|
|
17
21
|
export class MetaAg {
|
|
18
22
|
client;
|
|
@@ -52,6 +56,16 @@ export class MetaAg {
|
|
|
52
56
|
const { CetusProvider } = await import("./providers/cetus").catch(catchImportError(EProvider.CETUS));
|
|
53
57
|
this.providers[EProvider.CETUS] = new CetusProvider(providerOptions, this.options, this.client);
|
|
54
58
|
break;
|
|
59
|
+
case EProvider.OKX:
|
|
60
|
+
this.providers[EProvider.OKX] = new OkxProvider(providerOptions, this.options, this.client);
|
|
61
|
+
break;
|
|
62
|
+
case EProvider.BLUEFINX:
|
|
63
|
+
this.providers[EProvider.BLUEFINX] = new BluefinXProvider(providerOptions);
|
|
64
|
+
break;
|
|
65
|
+
case EProvider.ASTRO:
|
|
66
|
+
const { AstroProvider } = await import("./providers/astro").catch(catchImportError(EProvider.ASTRO));
|
|
67
|
+
this.providers[EProvider.ASTRO] = new AstroProvider(providerOptions);
|
|
68
|
+
break;
|
|
55
69
|
default:
|
|
56
70
|
throw new Error(`Provider not supported: ${provider}`);
|
|
57
71
|
}
|
|
@@ -59,51 +73,40 @@ export class MetaAg {
|
|
|
59
73
|
}
|
|
60
74
|
async _simulate(provider, quote, simulation) {
|
|
61
75
|
try {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
tx,
|
|
73
|
-
});
|
|
74
|
-
tx.add(metaSettle(quote, coinOut, 10000, this.options.tipBps, this.options.partner, this.options.partnerCommissionBps));
|
|
75
|
-
tx.transferObjects([coinOut], simulation.sender);
|
|
76
|
-
const res = await timeout(() => this.inspector.devInspectTransactionBlock({
|
|
77
|
-
sender: simulation.sender,
|
|
78
|
-
transactionBlock: tx,
|
|
79
|
-
}), simulation.timeout ?? 2000, `simulation for ${provider.kind} provider with id ${id}`);
|
|
80
|
-
if (res.effects.status.status === "failure") {
|
|
81
|
-
throw new Error(res.error ?? "Simulation failed");
|
|
76
|
+
if (isAggregatorProvider(provider)) {
|
|
77
|
+
return simulateAggregator(provider, quote, simulation, this.inspector, this.options);
|
|
78
|
+
}
|
|
79
|
+
switch (quote.provider) {
|
|
80
|
+
case EProvider.BLUEFINX:
|
|
81
|
+
return simulateBluefinX(quote);
|
|
82
|
+
case EProvider.OKX:
|
|
83
|
+
return simulateOKXSwap(quote, this.inspector, simulation, this.options);
|
|
84
|
+
default:
|
|
85
|
+
throw new Error(`Provider not supported: ${provider.kind}`);
|
|
82
86
|
}
|
|
83
|
-
const amountOut = extractAmountOutWrapper(res.events);
|
|
84
|
-
return {
|
|
85
|
-
id,
|
|
86
|
-
simulatedAmountOut: amountOut,
|
|
87
|
-
gasUsed: res.effects.gasUsed,
|
|
88
|
-
provider: provider.kind,
|
|
89
|
-
};
|
|
90
87
|
}
|
|
91
88
|
catch (error) {
|
|
92
|
-
console.warn(
|
|
89
|
+
console.warn(error, { provider: provider.kind, quote: quote.id });
|
|
93
90
|
}
|
|
94
91
|
}
|
|
95
92
|
async _quote(provider, options, simulation) {
|
|
96
93
|
const quote = await timeout(async () => {
|
|
97
94
|
const quote = await provider.quote(options);
|
|
95
|
+
if (!quote)
|
|
96
|
+
return null;
|
|
98
97
|
const { expectedAmount } = getExpectedReturn(quote.rawAmountOut, 0, this.options.partnerCommissionBps, this.options.tipBps);
|
|
99
98
|
quote.amountOut = expectedAmount;
|
|
100
99
|
return quote;
|
|
101
|
-
}, options.timeout ?? 2000, `quote for ${provider.kind} provider from ${options.
|
|
102
|
-
if (simulation) {
|
|
100
|
+
}, options.timeout ?? 2000, `quote for ${provider.kind} provider from ${options.coinTypeIn} to ${options.coinTypeOut}`);
|
|
101
|
+
if (quote && simulation) {
|
|
103
102
|
if (simulation.onSimulated) {
|
|
104
103
|
this._simulate(provider, quote, simulation).then((payload) => {
|
|
105
104
|
if (payload) {
|
|
106
|
-
|
|
105
|
+
setTimeout(() => {
|
|
106
|
+
quote.simulatedAmountOut = payload.simulatedAmountOut;
|
|
107
|
+
quote.gasUsed = payload.gasUsed;
|
|
108
|
+
simulation.onSimulated?.({ ...quote });
|
|
109
|
+
});
|
|
107
110
|
}
|
|
108
111
|
});
|
|
109
112
|
}
|
|
@@ -115,6 +118,29 @@ export class MetaAg {
|
|
|
115
118
|
}
|
|
116
119
|
return quote;
|
|
117
120
|
}
|
|
121
|
+
async _fastSwap({ quote, signer, useGasCoin, signTransaction }, getTransactionBlockParams) {
|
|
122
|
+
const tx = new Transaction();
|
|
123
|
+
const coin = await this.swap({
|
|
124
|
+
quote,
|
|
125
|
+
signer,
|
|
126
|
+
tx,
|
|
127
|
+
coinIn: coinWithBalance({
|
|
128
|
+
type: quote.coinTypeIn,
|
|
129
|
+
balance: BigInt(quote.amountIn),
|
|
130
|
+
useGasCoin,
|
|
131
|
+
}),
|
|
132
|
+
});
|
|
133
|
+
tx.transferObjects([coin], signer);
|
|
134
|
+
tx.setSenderIfNotSet(signer);
|
|
135
|
+
const txBytes = await tx.build({ client: this.client });
|
|
136
|
+
const { signature, bytes } = await signTransaction(toBase64(txBytes));
|
|
137
|
+
return this.client.executeTransactionBlock({
|
|
138
|
+
transactionBlock: bytes,
|
|
139
|
+
signature,
|
|
140
|
+
options: getTransactionBlockParams?.options,
|
|
141
|
+
signal: getTransactionBlockParams?.signal,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
118
144
|
/**
|
|
119
145
|
* Get quotes from all providers
|
|
120
146
|
* @param options - quote options
|
|
@@ -124,8 +150,8 @@ export class MetaAg {
|
|
|
124
150
|
async quote(options, simulation) {
|
|
125
151
|
const opts = {
|
|
126
152
|
...options,
|
|
127
|
-
|
|
128
|
-
|
|
153
|
+
coinTypeIn: normalizeStructTag(options.coinTypeIn),
|
|
154
|
+
coinTypeOut: normalizeStructTag(options.coinTypeOut),
|
|
129
155
|
};
|
|
130
156
|
const quotes = await Promise.allSettled(Object.entries(this.options.providers)
|
|
131
157
|
.filter(([_k, v]) => !v.disabled)
|
|
@@ -141,6 +167,8 @@ export class MetaAg {
|
|
|
141
167
|
}
|
|
142
168
|
/**
|
|
143
169
|
* Build transaction from quote
|
|
170
|
+
* @info Use this function to build composable transaction (ie: add more commands after the swap, consume the coin out object)
|
|
171
|
+
* @warning Providers that build transaction on the fly (typically RFQ, Swap-API providers ie: BluefinX, Okx, ...) are not supported, please use `fastSwap` instead
|
|
144
172
|
* @param options - build tx options
|
|
145
173
|
* @param slippageBps - slippage bps if not specified, fallback to global slippage bps, if none of them specified, default to 100
|
|
146
174
|
* @returns coin out object, you must consume it by transferObjects, or other sub sequence commands
|
|
@@ -148,11 +176,32 @@ export class MetaAg {
|
|
|
148
176
|
async swap(options, slippageBps) {
|
|
149
177
|
const provider = await this._getProvider(options.quote.provider);
|
|
150
178
|
assert(!!provider, `Provider not found: ${options.quote.provider}`);
|
|
179
|
+
assert(isAggregatorProvider(provider), `Provider does not support swap: ${provider.kind}`);
|
|
151
180
|
const coinOut = await provider.swap(options);
|
|
152
181
|
options.tx.add(metaSettle(options.quote, coinOut, slippageBps ?? this.options.slippageBps ?? 100, this.options.tipBps, this.options.partner, this.options.partnerCommissionBps));
|
|
153
182
|
options.tx.setSenderIfNotSet(options.signer);
|
|
154
183
|
return coinOut;
|
|
155
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* Build, Sign, and Execute transaction in one step
|
|
187
|
+
* @param options - fast swap options
|
|
188
|
+
* @returns - txDigest of the transaction
|
|
189
|
+
*/
|
|
190
|
+
async fastSwap(options, getTransactionBlockParams) {
|
|
191
|
+
const provider = await this._getProvider(options.quote.provider);
|
|
192
|
+
if (isAggregatorProvider(provider)) {
|
|
193
|
+
return this._fastSwap(options, getTransactionBlockParams);
|
|
194
|
+
}
|
|
195
|
+
else if (isSwapAPIProvider(provider)) {
|
|
196
|
+
return this.client.waitForTransaction({
|
|
197
|
+
...getTransactionBlockParams,
|
|
198
|
+
digest: await provider.fastSwap(options),
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
throw new Error(`Provider not supported: ${provider.kind}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
156
205
|
/**
|
|
157
206
|
* Update meta aggregator options
|
|
158
207
|
* @param options - update options payload
|
|
@@ -185,71 +234,17 @@ export class MetaAg {
|
|
|
185
234
|
}
|
|
186
235
|
}
|
|
187
236
|
}
|
|
188
|
-
/**
|
|
189
|
-
* this settlement does not charge commission fee for partner, since all integrated aggregators already charge commission fee for partner
|
|
190
|
-
* @param quote Meta Aggregator Quote
|
|
191
|
-
* @param coinOut Coin Out Object
|
|
192
|
-
* @param slippageBps Slippage Bps
|
|
193
|
-
* @param tipBps Tip Bps default = 0
|
|
194
|
-
* @param partner address of partner for analytic default is zero address
|
|
195
|
-
*/
|
|
196
|
-
const metaSettle = (quote, coinOut, slippageBps = 100, tipBps = 0, partner, commissionBps = 0) => {
|
|
197
|
-
return (tx) => {
|
|
198
|
-
const { minAmount, expectedAmount } = getExpectedReturn(quote.rawAmountOut, slippageBps, commissionBps, tipBps);
|
|
199
|
-
if (tipBps > 0) {
|
|
200
|
-
tx.moveCall({
|
|
201
|
-
target: `${_7K_META_PUBLISHED_AT}::vault::collect_tip`,
|
|
202
|
-
typeArguments: [quote.coinTypeOut],
|
|
203
|
-
arguments: [
|
|
204
|
-
tx.object(_7K_META_VAULT),
|
|
205
|
-
tx.object(_7K_META_CONFIG),
|
|
206
|
-
coinOut,
|
|
207
|
-
tx.pure.u64(tipBps),
|
|
208
|
-
],
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
tx.moveCall({
|
|
212
|
-
target: `${_7K_META_PUBLISHED_AT}::settle::settle`,
|
|
213
|
-
typeArguments: [quote.coinTypeIn, quote.coinTypeOut],
|
|
214
|
-
arguments: [
|
|
215
|
-
tx.object(_7K_META_CONFIG),
|
|
216
|
-
tx.object(_7K_META_VAULT),
|
|
217
|
-
tx.pure.u64(quote.amountIn),
|
|
218
|
-
coinOut,
|
|
219
|
-
tx.pure.u64(minAmount),
|
|
220
|
-
tx.pure.u64(expectedAmount),
|
|
221
|
-
tx.pure.option("address", partner),
|
|
222
|
-
tx.pure.u64(commissionBps),
|
|
223
|
-
tx.pure.u64(0), // ps
|
|
224
|
-
],
|
|
225
|
-
});
|
|
226
|
-
};
|
|
227
|
-
};
|
|
228
|
-
const extractAmountOutWrapper = (events) => {
|
|
229
|
-
const swapEvent = events
|
|
230
|
-
.filter((event) => event.type === `${_7K_META_PACKAGE_ID}::settle::Swap`)
|
|
231
|
-
?.pop();
|
|
232
|
-
return swapEvent?.parsedJson?.amount_out;
|
|
233
|
-
};
|
|
234
237
|
const catchImportError = (provider) => {
|
|
235
238
|
return (e) => {
|
|
236
239
|
const map = {
|
|
237
240
|
[EProvider.CETUS]: "@cetusprotocol/aggregator-sdk",
|
|
238
241
|
[EProvider.FLOWX]: "@flowx-finance/sdk",
|
|
239
242
|
[EProvider.BLUEFIN7K]: "@7kprotocol/sdk-ts",
|
|
243
|
+
[EProvider.ASTRO]: "@naviprotocol/astros-aggregator-sdk",
|
|
244
|
+
[EProvider.OKX]: "",
|
|
245
|
+
[EProvider.BLUEFINX]: "",
|
|
240
246
|
};
|
|
241
247
|
console.warn(`Please install ${map[provider]} to use ${provider} provider`);
|
|
242
248
|
throw e;
|
|
243
249
|
};
|
|
244
250
|
};
|
|
245
|
-
const timeout = async (fn, timeout, msg) => {
|
|
246
|
-
if (timeout <= 0)
|
|
247
|
-
return fn();
|
|
248
|
-
return new Promise((resolve, reject) => {
|
|
249
|
-
const timer = setTimeout(() => reject(new Error(`Timeout ${msg ?? "operation"}`)), timeout);
|
|
250
|
-
fn()
|
|
251
|
-
.then(resolve)
|
|
252
|
-
.catch(reject)
|
|
253
|
-
.finally(() => clearTimeout(timer));
|
|
254
|
-
});
|
|
255
|
-
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { buildSwapPTBFromQuote, getQuote, } from "@naviprotocol/astros-aggregator-sdk";
|
|
2
|
+
import { v4 } from "uuid";
|
|
3
|
+
import { _7K_PARTNER_ADDRESS } from "../../../constants/_7k";
|
|
4
|
+
import { EProvider, } from "../../../types/metaAg";
|
|
5
|
+
import { assert } from "../../../utils/condition";
|
|
6
|
+
export class AstroProvider {
|
|
7
|
+
options;
|
|
8
|
+
kind = EProvider.ASTRO;
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.options = options;
|
|
11
|
+
}
|
|
12
|
+
async quote({ amountIn, coinTypeIn, coinTypeOut, }) {
|
|
13
|
+
const quote = await getQuote(coinTypeIn, coinTypeOut, amountIn, this.options.api, {
|
|
14
|
+
baseUrl: this.options.api,
|
|
15
|
+
byAmountIn: true,
|
|
16
|
+
depth: this.options.depth,
|
|
17
|
+
dexList: this.options.dexList,
|
|
18
|
+
serviceFee: {
|
|
19
|
+
fee: 0,
|
|
20
|
+
receiverAddress: _7K_PARTNER_ADDRESS,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
id: v4(),
|
|
25
|
+
provider: this.kind,
|
|
26
|
+
quote,
|
|
27
|
+
amountIn,
|
|
28
|
+
rawAmountOut: quote.amount_out.toString(),
|
|
29
|
+
amountOut: quote.amount_out.toString(),
|
|
30
|
+
coinTypeIn,
|
|
31
|
+
coinTypeOut,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
async swap({ signer, quote, coinIn, tx }) {
|
|
35
|
+
assert(quote.provider === EProvider.ASTRO, "Invalid quote");
|
|
36
|
+
const coin = await buildSwapPTBFromQuote(signer, tx, 0, coinIn, quote.quote);
|
|
37
|
+
return coin;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -2,7 +2,6 @@ import { SuiPriceServiceConnection, SuiPythClient, } from "@pythnetwork/pyth-sui
|
|
|
2
2
|
import { v4 } from "uuid";
|
|
3
3
|
import { Config } from "../../../config";
|
|
4
4
|
import { _7K_PARTNER_ADDRESS } from "../../../constants/_7k";
|
|
5
|
-
import { API_ENDPOINTS } from "../../../constants/apiEndpoints";
|
|
6
5
|
import { EProvider, } from "../../../types/metaAg";
|
|
7
6
|
import { assert } from "../../../utils/condition";
|
|
8
7
|
import { buildTxV2 } from "../../swap/buildTxV2";
|
|
@@ -20,6 +19,8 @@ export class BluefinProvider {
|
|
|
20
19
|
const pythConnection = new SuiPriceServiceConnection(this.metaOptions.hermesApi);
|
|
21
20
|
if (options.apiKey)
|
|
22
21
|
Config.setApiKey(options.apiKey);
|
|
22
|
+
if (options.api)
|
|
23
|
+
Config.setApi(options.api);
|
|
23
24
|
Config.setSuiClient(client);
|
|
24
25
|
Config.setPythClient(pythClient);
|
|
25
26
|
Config.setPythConnection(pythConnection);
|
|
@@ -27,9 +28,8 @@ export class BluefinProvider {
|
|
|
27
28
|
async quote(options) {
|
|
28
29
|
const quote = await getQuote({
|
|
29
30
|
amountIn: options.amountIn,
|
|
30
|
-
tokenIn: options.
|
|
31
|
-
tokenOut: options.
|
|
32
|
-
api: this.options.api || API_ENDPOINTS.MAIN,
|
|
31
|
+
tokenIn: options.coinTypeIn,
|
|
32
|
+
tokenOut: options.coinTypeOut,
|
|
33
33
|
sources: this.options.sources,
|
|
34
34
|
maxPaths: this.options.maxPaths,
|
|
35
35
|
excludedPools: this.options.excludedPools,
|
|
@@ -42,8 +42,8 @@ export class BluefinProvider {
|
|
|
42
42
|
amountIn: quote.swapAmountWithDecimal,
|
|
43
43
|
rawAmountOut: quote.returnAmountWithDecimal,
|
|
44
44
|
amountOut: quote.returnAmountWithDecimal,
|
|
45
|
-
coinTypeIn: options.
|
|
46
|
-
coinTypeOut: options.
|
|
45
|
+
coinTypeIn: options.coinTypeIn,
|
|
46
|
+
coinTypeOut: options.coinTypeOut,
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
async swap({ quote, signer, tx, coinIn }) {
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { coinWithBalance, Transaction } from "@mysten/sui/transactions";
|
|
2
|
+
import { isValidSuiAddress } from "@mysten/sui/utils";
|
|
3
|
+
import { v4 } from "uuid";
|
|
4
|
+
import { Config } from "../../../config";
|
|
5
|
+
import { _7K_PARTNER_ADDRESS } from "../../../constants/_7k";
|
|
6
|
+
import { executeBluefinTx } from "../../../libs/protocols/bluefinx/client";
|
|
7
|
+
import { BluefinXTx } from "../../../types/aggregator";
|
|
8
|
+
import { EProvider, } from "../../../types/metaAg";
|
|
9
|
+
import { assert } from "../../../utils/condition";
|
|
10
|
+
import { buildTxV2Int } from "../../swap/buildTxV2";
|
|
11
|
+
import { getQuote } from "../../swap/getQuote";
|
|
12
|
+
import { metaSettle } from "../common";
|
|
13
|
+
const SUPPORT_COINS = [
|
|
14
|
+
"0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI",
|
|
15
|
+
"0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC",
|
|
16
|
+
];
|
|
17
|
+
export class BluefinXProvider {
|
|
18
|
+
options;
|
|
19
|
+
kind = EProvider.BLUEFINX;
|
|
20
|
+
constructor(options) {
|
|
21
|
+
this.options = options;
|
|
22
|
+
if (options.apiKey)
|
|
23
|
+
Config.setApiKey(options.apiKey);
|
|
24
|
+
if (options.api)
|
|
25
|
+
Config.setApi(options.api);
|
|
26
|
+
}
|
|
27
|
+
async quote(options) {
|
|
28
|
+
if (!this.canQuote(options))
|
|
29
|
+
return null;
|
|
30
|
+
const quote = await getQuote({
|
|
31
|
+
amountIn: options.amountIn,
|
|
32
|
+
tokenIn: options.coinTypeIn,
|
|
33
|
+
tokenOut: options.coinTypeOut,
|
|
34
|
+
sources: ["bluefinx"],
|
|
35
|
+
taker: options.signer,
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
id: v4(),
|
|
39
|
+
provider: this.kind,
|
|
40
|
+
quote,
|
|
41
|
+
amountIn: quote.swapAmountWithDecimal,
|
|
42
|
+
rawAmountOut: quote.returnAmountWithDecimal,
|
|
43
|
+
amountOut: quote.returnAmountWithDecimal,
|
|
44
|
+
coinTypeIn: options.coinTypeIn,
|
|
45
|
+
coinTypeOut: options.coinTypeOut,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
canQuote(options) {
|
|
49
|
+
return (!!options.signer &&
|
|
50
|
+
isValidSuiAddress(options.signer) &&
|
|
51
|
+
SUPPORT_COINS.includes(options.coinTypeIn) &&
|
|
52
|
+
SUPPORT_COINS.includes(options.coinTypeOut));
|
|
53
|
+
}
|
|
54
|
+
async fastSwap(options) {
|
|
55
|
+
assert(options.quote.provider === this.kind, "Invalid BluefinX quote");
|
|
56
|
+
const quote = options.quote.quote;
|
|
57
|
+
const tx = new Transaction();
|
|
58
|
+
const { tx: bluefinTx } = await buildTxV2Int({
|
|
59
|
+
quoteResponse: quote,
|
|
60
|
+
accountAddress: options.signer,
|
|
61
|
+
commission: { commissionBps: 0, partner: _7K_PARTNER_ADDRESS },
|
|
62
|
+
slippage: 0,
|
|
63
|
+
extendTx: {
|
|
64
|
+
tx,
|
|
65
|
+
coinIn: coinWithBalance({
|
|
66
|
+
type: options.quote.coinTypeIn,
|
|
67
|
+
balance: BigInt(options.quote.amountIn),
|
|
68
|
+
useGasCoin: false,
|
|
69
|
+
}),
|
|
70
|
+
},
|
|
71
|
+
}, (tx, coinOut) => {
|
|
72
|
+
if (coinOut) {
|
|
73
|
+
tx.add(metaSettle(options.quote, coinOut, 0, 0));
|
|
74
|
+
tx.transferObjects([coinOut], options.signer);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
assert(bluefinTx instanceof BluefinXTx, "BluefinX transaction not found");
|
|
78
|
+
const { signature } = await options.signTransaction(bluefinTx.txBytes);
|
|
79
|
+
const res = await executeBluefinTx(bluefinTx, signature);
|
|
80
|
+
assert(res.approved, "BluefinX transaction not approved");
|
|
81
|
+
assert(res.txDigest, "BluefinX transaction digest not found");
|
|
82
|
+
return res.txDigest;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -23,8 +23,8 @@ export class CetusProvider {
|
|
|
23
23
|
const quote = await this.cetusClient.findRouters({
|
|
24
24
|
amount: quoteOptions.amountIn,
|
|
25
25
|
byAmountIn: true,
|
|
26
|
-
from: quoteOptions.
|
|
27
|
-
target: quoteOptions.
|
|
26
|
+
from: quoteOptions.coinTypeIn,
|
|
27
|
+
target: quoteOptions.coinTypeOut,
|
|
28
28
|
providers: this.options.sources,
|
|
29
29
|
splitCount: this.options.splitCount,
|
|
30
30
|
splitAlgorithm: this.options.splitAlgorithm,
|
|
@@ -40,8 +40,8 @@ export class CetusProvider {
|
|
|
40
40
|
amountIn: quote.amountIn.toString() || "0",
|
|
41
41
|
rawAmountOut: quote.amountOut.toString() || "0",
|
|
42
42
|
amountOut: quote.amountOut.toString() || "0",
|
|
43
|
-
coinTypeIn: quoteOptions.
|
|
44
|
-
coinTypeOut: quoteOptions.
|
|
43
|
+
coinTypeIn: quoteOptions.coinTypeIn,
|
|
44
|
+
coinTypeOut: quoteOptions.coinTypeOut,
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
async swap(options) {
|
|
@@ -16,8 +16,8 @@ export class FlowxProvider {
|
|
|
16
16
|
async quote(quoteOptions) {
|
|
17
17
|
const quote = await this.quoter.getRoutes({
|
|
18
18
|
amountIn: quoteOptions.amountIn,
|
|
19
|
-
tokenIn: quoteOptions.
|
|
20
|
-
tokenOut: quoteOptions.
|
|
19
|
+
tokenIn: quoteOptions.coinTypeIn,
|
|
20
|
+
tokenOut: quoteOptions.coinTypeOut,
|
|
21
21
|
includeSources: this.options.sources,
|
|
22
22
|
excludePools: this.options.excludePools,
|
|
23
23
|
excludeSources: this.options.excludeSources,
|
|
@@ -31,8 +31,8 @@ export class FlowxProvider {
|
|
|
31
31
|
amountIn: quote.amountIn?.toString() ?? "0",
|
|
32
32
|
rawAmountOut: quote.amountOut?.toString() ?? "0",
|
|
33
33
|
amountOut: quote.amountOut?.toString() ?? "0",
|
|
34
|
-
coinTypeIn: quoteOptions.
|
|
35
|
-
coinTypeOut: quoteOptions.
|
|
34
|
+
coinTypeIn: quoteOptions.coinTypeIn,
|
|
35
|
+
coinTypeOut: quoteOptions.coinTypeOut,
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
async swap(options) {
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { Commands, Transaction, TransactionDataBuilder, } from "@mysten/sui/transactions";
|
|
2
|
+
import { normalizeStructTag, normalizeSuiAddress, toBase64, } from "@mysten/sui/utils";
|
|
3
|
+
import { v4 } from "uuid";
|
|
4
|
+
import { SUI_TYPE } from "../../../constants/tokens";
|
|
5
|
+
import { EProvider, } from "../../../types/metaAg";
|
|
6
|
+
import { assert } from "../../../utils/condition";
|
|
7
|
+
import { metaSettle, simulateSwapTx } from "../common";
|
|
8
|
+
const API = "https://web3.okx.com";
|
|
9
|
+
const SWAP_PATH = "/api/v5/dex/aggregator/swap";
|
|
10
|
+
const CHAIN_ID = "784";
|
|
11
|
+
const NORMALIZED_SUI_TYPE = normalizeStructTag(SUI_TYPE);
|
|
12
|
+
export class OkxProvider {
|
|
13
|
+
options;
|
|
14
|
+
metaOptions;
|
|
15
|
+
client;
|
|
16
|
+
kind = EProvider.OKX;
|
|
17
|
+
constructor(options, metaOptions, client) {
|
|
18
|
+
this.options = options;
|
|
19
|
+
this.metaOptions = metaOptions;
|
|
20
|
+
this.client = client;
|
|
21
|
+
}
|
|
22
|
+
async quote({ amountIn, coinTypeIn, coinTypeOut, signer, }) {
|
|
23
|
+
if (!signer)
|
|
24
|
+
return null;
|
|
25
|
+
const request = {
|
|
26
|
+
chainId: CHAIN_ID,
|
|
27
|
+
amount: amountIn,
|
|
28
|
+
fromTokenAddress: coinTypeIn === NORMALIZED_SUI_TYPE ? SUI_TYPE : coinTypeIn,
|
|
29
|
+
toTokenAddress: coinTypeOut === NORMALIZED_SUI_TYPE ? SUI_TYPE : coinTypeOut,
|
|
30
|
+
slippage: (this.metaOptions.slippageBps / 10000).toString(),
|
|
31
|
+
userWalletAddress: signer,
|
|
32
|
+
};
|
|
33
|
+
const queryString = "?" + new URLSearchParams(request).toString();
|
|
34
|
+
const url = `${this.options.api ?? API}${SWAP_PATH}${queryString}`;
|
|
35
|
+
const response = await fetch(url, {
|
|
36
|
+
headers: await getHeaders(this.options, "GET", SWAP_PATH, queryString),
|
|
37
|
+
});
|
|
38
|
+
const quote = (await response.json());
|
|
39
|
+
assert(quote.code === "0" && quote.data.length > 0, "No quote found");
|
|
40
|
+
return {
|
|
41
|
+
id: v4(),
|
|
42
|
+
provider: this.kind,
|
|
43
|
+
coinTypeIn,
|
|
44
|
+
coinTypeOut,
|
|
45
|
+
amountIn,
|
|
46
|
+
amountOut: quote.data[0].routerResult.toTokenAmount,
|
|
47
|
+
rawAmountOut: quote.data[0].routerResult.toTokenAmount,
|
|
48
|
+
quote: quote.data[0],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async fastSwap(options) {
|
|
52
|
+
const { quote, signer, signTransaction } = options;
|
|
53
|
+
assert(quote.provider === EProvider.OKX, "Invalid quote");
|
|
54
|
+
const { tx, coin } = buildTx({ quote, signer });
|
|
55
|
+
tx.add(metaSettle(quote, coin, this.metaOptions.slippageBps, this.metaOptions.tipBps, this.metaOptions.partner, this.metaOptions.partnerCommissionBps));
|
|
56
|
+
tx.transferObjects([coin], signer);
|
|
57
|
+
const txBytes = await tx.build({ client: this.client });
|
|
58
|
+
const { bytes, signature } = await signTransaction(toBase64(txBytes));
|
|
59
|
+
const res = await this.client.executeTransactionBlock({
|
|
60
|
+
signature,
|
|
61
|
+
transactionBlock: bytes,
|
|
62
|
+
});
|
|
63
|
+
return res.digest;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function generateHmacSha256(message, secretKey) {
|
|
67
|
+
const encoder = new TextEncoder();
|
|
68
|
+
const encodeBase64 = (bytes) => {
|
|
69
|
+
if (typeof Buffer !== "undefined") {
|
|
70
|
+
return Buffer.from(bytes).toString("base64");
|
|
71
|
+
}
|
|
72
|
+
let binary = "";
|
|
73
|
+
for (let i = 0; i < bytes.byteLength; i++) {
|
|
74
|
+
binary += String.fromCharCode(bytes[i]);
|
|
75
|
+
}
|
|
76
|
+
if (typeof btoa !== "undefined") {
|
|
77
|
+
return btoa(binary);
|
|
78
|
+
}
|
|
79
|
+
throw new Error("Base64 encoder not available in this environment");
|
|
80
|
+
};
|
|
81
|
+
if (typeof globalThis !== "undefined" && globalThis.crypto?.subtle) {
|
|
82
|
+
const key = await globalThis.crypto.subtle.importKey("raw", encoder.encode(secretKey), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
83
|
+
const signature = await globalThis.crypto.subtle.sign("HMAC", key, encoder.encode(message));
|
|
84
|
+
return encodeBase64(new Uint8Array(signature));
|
|
85
|
+
}
|
|
86
|
+
const { createHmac } = await import("node:crypto");
|
|
87
|
+
return createHmac("sha256", secretKey).update(message).digest("base64");
|
|
88
|
+
}
|
|
89
|
+
async function getHeaders(options, method, requestPath, queryString = "") {
|
|
90
|
+
const { apiKey, secretKey, apiPassphrase, projectId } = options;
|
|
91
|
+
if (!apiKey || !secretKey || !apiPassphrase || !projectId) {
|
|
92
|
+
throw new Error("Missing required environment variables");
|
|
93
|
+
}
|
|
94
|
+
const timestamp = new Date().toISOString();
|
|
95
|
+
const stringToSign = timestamp + method + requestPath + queryString;
|
|
96
|
+
return {
|
|
97
|
+
"Content-Type": "application/json",
|
|
98
|
+
"OK-ACCESS-KEY": apiKey,
|
|
99
|
+
"OK-ACCESS-SIGN": await generateHmacSha256(stringToSign, secretKey),
|
|
100
|
+
"OK-ACCESS-TIMESTAMP": timestamp,
|
|
101
|
+
"OK-ACCESS-PASSPHRASE": apiPassphrase,
|
|
102
|
+
"OK-ACCESS-PROJECT": projectId,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const replaceFinalizeCommand = (tx, packageId) => {
|
|
106
|
+
const builder = TransactionDataBuilder.restore(tx.getData());
|
|
107
|
+
const i = builder.commands.findIndex((cmd) => cmd.$kind === "MoveCall" &&
|
|
108
|
+
normalizeSuiAddress(cmd.MoveCall.package) ===
|
|
109
|
+
normalizeSuiAddress(packageId) &&
|
|
110
|
+
cmd.MoveCall.module === "router" &&
|
|
111
|
+
cmd.MoveCall.function === "finalize");
|
|
112
|
+
const cmd = builder.commands[i];
|
|
113
|
+
assert(cmd.MoveCall, "OKX: Finalize command not found");
|
|
114
|
+
builder.replaceCommand(i, Commands.MoveCall({
|
|
115
|
+
package: packageId,
|
|
116
|
+
module: "router",
|
|
117
|
+
function: "finalize_without_transfer",
|
|
118
|
+
typeArguments: cmd.MoveCall.typeArguments,
|
|
119
|
+
arguments: [
|
|
120
|
+
cmd.MoveCall.arguments[0],
|
|
121
|
+
cmd.MoveCall.arguments[5],
|
|
122
|
+
cmd.MoveCall.arguments[6],
|
|
123
|
+
],
|
|
124
|
+
}));
|
|
125
|
+
const tx2 = Transaction.from(builder.build());
|
|
126
|
+
return { tx: tx2, coin: { NestedResult: [i, 0] } };
|
|
127
|
+
};
|
|
128
|
+
const buildTx = (options) => {
|
|
129
|
+
const { quote, signer } = options;
|
|
130
|
+
assert(quote.provider === EProvider.OKX, "Invalid quote");
|
|
131
|
+
const tx = Transaction.from(quote.quote.tx.data);
|
|
132
|
+
tx.setSenderIfNotSet(signer);
|
|
133
|
+
const { tx: tx2, coin } = replaceFinalizeCommand(tx, quote.quote.tx.to);
|
|
134
|
+
return { tx: tx2, coin };
|
|
135
|
+
};
|
|
136
|
+
export const simulateOKXSwap = async (quote, inspector, simulation, metaOptions) => {
|
|
137
|
+
const { tx, coin } = buildTx({ quote, signer: quote.quote.tx.from });
|
|
138
|
+
tx.add(metaSettle(quote, coin, 10000, metaOptions.tipBps, metaOptions.partner, metaOptions.partnerCommissionBps));
|
|
139
|
+
tx.transferObjects([coin], quote.quote.tx.from);
|
|
140
|
+
const res = await simulateSwapTx(tx, inspector, simulation);
|
|
141
|
+
return {
|
|
142
|
+
id: quote.id,
|
|
143
|
+
provider: quote.provider,
|
|
144
|
+
...res,
|
|
145
|
+
};
|
|
146
|
+
};
|
|
@@ -168,6 +168,9 @@ export const buildBluefinXTx = async (tx, accountAddress, quoteResponse) => {
|
|
|
168
168
|
if (extra.quoteExpiresAtUtcMillis < Date.now()) {
|
|
169
169
|
throw new Error("Quote expired");
|
|
170
170
|
}
|
|
171
|
+
if (extra.taker !== accountAddress) {
|
|
172
|
+
throw new Error("Sender mismatch with quote");
|
|
173
|
+
}
|
|
171
174
|
tx.setSenderIfNotSet(accountAddress);
|
|
172
175
|
const bytes = await tx.build({
|
|
173
176
|
client: Config.getSuiClient(),
|
|
@@ -12,7 +12,10 @@ import { getConfig } from "./config";
|
|
|
12
12
|
* 2. Merging redundant swaps to the same pool within each wave
|
|
13
13
|
* 3. Processing waves sequentially, passing intermediate tokens between waves
|
|
14
14
|
*/
|
|
15
|
-
export const buildTxV2 = async (
|
|
15
|
+
export const buildTxV2 = async (params) => {
|
|
16
|
+
return buildTxV2Int(params);
|
|
17
|
+
};
|
|
18
|
+
export const buildTxV2Int = async ({ quoteResponse, accountAddress, slippage, commission: __commission, devInspect, extendTx, isSponsored, }, beforeBuildBluefinXTx) => {
|
|
16
19
|
const isBluefinX = isBluefinXRouting(quoteResponse);
|
|
17
20
|
const _commission = {
|
|
18
21
|
...__commission,
|
|
@@ -58,6 +61,7 @@ export const buildTxV2 = async ({ quoteResponse, accountAddress, slippage, commi
|
|
|
58
61
|
}
|
|
59
62
|
}
|
|
60
63
|
if (isBluefinX) {
|
|
64
|
+
beforeBuildBluefinXTx?.(tx, coinOut);
|
|
61
65
|
return {
|
|
62
66
|
tx: await buildBluefinXTx(tx, accountAddress, quoteResponse),
|
|
63
67
|
coinOut,
|
|
@@ -66,7 +70,7 @@ export const buildTxV2 = async ({ quoteResponse, accountAddress, slippage, commi
|
|
|
66
70
|
tx.setSenderIfNotSet(accountAddress);
|
|
67
71
|
return { tx, coinOut };
|
|
68
72
|
};
|
|
69
|
-
|
|
73
|
+
const optimize = async (pythMap, config, routes, coinData, tx, accountAddress) => {
|
|
70
74
|
// Initialize route states with split coins
|
|
71
75
|
const routeStates = routes.map((route, index) => ({
|
|
72
76
|
routeIndex: index,
|