@riftresearch/sdk 0.1.1
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 +25 -0
- package/dist/index.d.ts +358 -0
- package/dist/index.js +1390 -0
- package/package.json +75 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1390 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
19
|
+
|
|
20
|
+
// node_modules/@cowprotocol/sdk-common/dist/index.js
|
|
21
|
+
var require_dist = __commonJS((exports, module) => {
|
|
22
|
+
var __defProp2 = Object.defineProperty;
|
|
23
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
24
|
+
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
25
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
26
|
+
var __export = (target, all) => {
|
|
27
|
+
for (var name in all)
|
|
28
|
+
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
29
|
+
};
|
|
30
|
+
var __copyProps = (to, from, except, desc) => {
|
|
31
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
32
|
+
for (let key of __getOwnPropNames2(from))
|
|
33
|
+
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
34
|
+
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
35
|
+
}
|
|
36
|
+
return to;
|
|
37
|
+
};
|
|
38
|
+
var __toCommonJS = (mod) => __copyProps(__defProp2({}, "__esModule", { value: true }), mod);
|
|
39
|
+
var src_exports = {};
|
|
40
|
+
__export(src_exports, {
|
|
41
|
+
AbstractProviderAdapter: () => AbstractProviderAdapter,
|
|
42
|
+
AbstractSigner: () => AbstractSigner,
|
|
43
|
+
AdapterContext: () => AdapterContext,
|
|
44
|
+
AdapterUtils: () => AdapterUtils,
|
|
45
|
+
ContractFactory: () => ContractFactory,
|
|
46
|
+
CowError: () => CowError,
|
|
47
|
+
ERC20_ALLOWANCE_ABI: () => ERC20_ALLOWANCE_ABI,
|
|
48
|
+
ERC20_APPROVE_ABI: () => ERC20_APPROVE_ABI,
|
|
49
|
+
EthFlowAbi: () => EthFlowAbi,
|
|
50
|
+
GPV2SettlementAbi: () => GPV2SettlementAbi,
|
|
51
|
+
MAX_UINT256: () => MAX_UINT256,
|
|
52
|
+
MAX_UINT32: () => MAX_UINT32,
|
|
53
|
+
ONE: () => ONE,
|
|
54
|
+
TTLCache: () => TTLCache,
|
|
55
|
+
ZERO: () => ZERO,
|
|
56
|
+
ZERO_ADDRESS: () => ZERO_ADDRESS,
|
|
57
|
+
ZERO_HASH: () => ZERO_HASH,
|
|
58
|
+
applyPercentage: () => applyPercentage,
|
|
59
|
+
bpsToPercentage: () => bpsToPercentage,
|
|
60
|
+
enableLogging: () => enableLogging,
|
|
61
|
+
getGlobalAdapter: () => getGlobalAdapter,
|
|
62
|
+
isValidPrivateKey: () => isValidPrivateKey,
|
|
63
|
+
jsonWithBigintReplacer: () => jsonWithBigintReplacer,
|
|
64
|
+
log: () => log,
|
|
65
|
+
normalizePrivateKey: () => normalizePrivateKey,
|
|
66
|
+
percentageToBps: () => percentageToBps,
|
|
67
|
+
setGlobalAdapter: () => setGlobalAdapter
|
|
68
|
+
});
|
|
69
|
+
module.exports = __toCommonJS(src_exports);
|
|
70
|
+
var AbstractProviderAdapter = class {
|
|
71
|
+
ZERO_ADDRESS;
|
|
72
|
+
};
|
|
73
|
+
var AdapterUtils = class {
|
|
74
|
+
};
|
|
75
|
+
var MAX_UINT256 = BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
|
|
76
|
+
var EthFlowAbi = [
|
|
77
|
+
{
|
|
78
|
+
inputs: [
|
|
79
|
+
{
|
|
80
|
+
components: [
|
|
81
|
+
{
|
|
82
|
+
internalType: "contract IERC20",
|
|
83
|
+
name: "buyToken",
|
|
84
|
+
type: "address"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
internalType: "address",
|
|
88
|
+
name: "receiver",
|
|
89
|
+
type: "address"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
internalType: "uint256",
|
|
93
|
+
name: "sellAmount",
|
|
94
|
+
type: "uint256"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
internalType: "uint256",
|
|
98
|
+
name: "buyAmount",
|
|
99
|
+
type: "uint256"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
internalType: "bytes32",
|
|
103
|
+
name: "appData",
|
|
104
|
+
type: "bytes32"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
internalType: "uint256",
|
|
108
|
+
name: "feeAmount",
|
|
109
|
+
type: "uint256"
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
internalType: "uint32",
|
|
113
|
+
name: "validTo",
|
|
114
|
+
type: "uint32"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
internalType: "bool",
|
|
118
|
+
name: "partiallyFillable",
|
|
119
|
+
type: "bool"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
internalType: "int64",
|
|
123
|
+
name: "quoteId",
|
|
124
|
+
type: "int64"
|
|
125
|
+
}
|
|
126
|
+
],
|
|
127
|
+
internalType: "struct EthFlowOrder.Data",
|
|
128
|
+
name: "order",
|
|
129
|
+
type: "tuple"
|
|
130
|
+
}
|
|
131
|
+
],
|
|
132
|
+
name: "createOrder",
|
|
133
|
+
outputs: [
|
|
134
|
+
{
|
|
135
|
+
internalType: "bytes32",
|
|
136
|
+
name: "orderHash",
|
|
137
|
+
type: "bytes32"
|
|
138
|
+
}
|
|
139
|
+
],
|
|
140
|
+
stateMutability: "payable",
|
|
141
|
+
type: "function"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
inputs: [
|
|
145
|
+
{
|
|
146
|
+
components: [
|
|
147
|
+
{
|
|
148
|
+
internalType: "contract IERC20",
|
|
149
|
+
name: "buyToken",
|
|
150
|
+
type: "address"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
internalType: "address",
|
|
154
|
+
name: "receiver",
|
|
155
|
+
type: "address"
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
internalType: "uint256",
|
|
159
|
+
name: "sellAmount",
|
|
160
|
+
type: "uint256"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
internalType: "uint256",
|
|
164
|
+
name: "buyAmount",
|
|
165
|
+
type: "uint256"
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
internalType: "bytes32",
|
|
169
|
+
name: "appData",
|
|
170
|
+
type: "bytes32"
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
internalType: "uint256",
|
|
174
|
+
name: "feeAmount",
|
|
175
|
+
type: "uint256"
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
internalType: "uint32",
|
|
179
|
+
name: "validTo",
|
|
180
|
+
type: "uint32"
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
internalType: "bool",
|
|
184
|
+
name: "partiallyFillable",
|
|
185
|
+
type: "bool"
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
internalType: "int64",
|
|
189
|
+
name: "quoteId",
|
|
190
|
+
type: "int64"
|
|
191
|
+
}
|
|
192
|
+
],
|
|
193
|
+
internalType: "struct EthFlowOrder.Data",
|
|
194
|
+
name: "order",
|
|
195
|
+
type: "tuple"
|
|
196
|
+
}
|
|
197
|
+
],
|
|
198
|
+
name: "invalidateOrder",
|
|
199
|
+
outputs: [],
|
|
200
|
+
stateMutability: "nonpayable",
|
|
201
|
+
type: "function"
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
inputs: [
|
|
205
|
+
{
|
|
206
|
+
internalType: "bytes32",
|
|
207
|
+
name: "",
|
|
208
|
+
type: "bytes32"
|
|
209
|
+
}
|
|
210
|
+
],
|
|
211
|
+
name: "orders",
|
|
212
|
+
outputs: [
|
|
213
|
+
{
|
|
214
|
+
internalType: "address",
|
|
215
|
+
name: "owner",
|
|
216
|
+
type: "address"
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
internalType: "uint32",
|
|
220
|
+
name: "validTo",
|
|
221
|
+
type: "uint32"
|
|
222
|
+
}
|
|
223
|
+
],
|
|
224
|
+
stateMutability: "view",
|
|
225
|
+
type: "function"
|
|
226
|
+
}
|
|
227
|
+
];
|
|
228
|
+
var GPV2SettlementAbi = [
|
|
229
|
+
{
|
|
230
|
+
anonymous: false,
|
|
231
|
+
inputs: [
|
|
232
|
+
{
|
|
233
|
+
indexed: true,
|
|
234
|
+
internalType: "address",
|
|
235
|
+
name: "owner",
|
|
236
|
+
type: "address"
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
indexed: false,
|
|
240
|
+
internalType: "contract IERC20",
|
|
241
|
+
name: "sellToken",
|
|
242
|
+
type: "address"
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
indexed: false,
|
|
246
|
+
internalType: "contract IERC20",
|
|
247
|
+
name: "buyToken",
|
|
248
|
+
type: "address"
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
indexed: false,
|
|
252
|
+
internalType: "uint256",
|
|
253
|
+
name: "sellAmount",
|
|
254
|
+
type: "uint256"
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
indexed: false,
|
|
258
|
+
internalType: "uint256",
|
|
259
|
+
name: "buyAmount",
|
|
260
|
+
type: "uint256"
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
indexed: false,
|
|
264
|
+
internalType: "uint256",
|
|
265
|
+
name: "feeAmount",
|
|
266
|
+
type: "uint256"
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
indexed: false,
|
|
270
|
+
internalType: "bytes",
|
|
271
|
+
name: "orderUid",
|
|
272
|
+
type: "bytes"
|
|
273
|
+
}
|
|
274
|
+
],
|
|
275
|
+
name: "Trade",
|
|
276
|
+
type: "event"
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
inputs: [
|
|
280
|
+
{
|
|
281
|
+
internalType: "bytes",
|
|
282
|
+
name: "orderUid",
|
|
283
|
+
type: "bytes"
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
internalType: "bool",
|
|
287
|
+
name: "signed",
|
|
288
|
+
type: "bool"
|
|
289
|
+
}
|
|
290
|
+
],
|
|
291
|
+
name: "setPreSignature",
|
|
292
|
+
outputs: [],
|
|
293
|
+
stateMutability: "nonpayable",
|
|
294
|
+
type: "function"
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
inputs: [
|
|
298
|
+
{
|
|
299
|
+
internalType: "bytes",
|
|
300
|
+
name: "orderUid",
|
|
301
|
+
type: "bytes"
|
|
302
|
+
}
|
|
303
|
+
],
|
|
304
|
+
name: "invalidateOrder",
|
|
305
|
+
outputs: [],
|
|
306
|
+
stateMutability: "nonpayable",
|
|
307
|
+
type: "function"
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
inputs: [],
|
|
311
|
+
name: "domainSeparator",
|
|
312
|
+
outputs: [{ name: "", type: "bytes32" }],
|
|
313
|
+
stateMutability: "nonpayable",
|
|
314
|
+
type: "function"
|
|
315
|
+
}
|
|
316
|
+
];
|
|
317
|
+
var ERC20_APPROVE_ABI = [
|
|
318
|
+
{
|
|
319
|
+
constant: false,
|
|
320
|
+
inputs: [
|
|
321
|
+
{
|
|
322
|
+
name: "_spender",
|
|
323
|
+
type: "address"
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
name: "_value",
|
|
327
|
+
type: "uint256"
|
|
328
|
+
}
|
|
329
|
+
],
|
|
330
|
+
name: "approve",
|
|
331
|
+
outputs: [
|
|
332
|
+
{
|
|
333
|
+
name: "",
|
|
334
|
+
type: "bool"
|
|
335
|
+
}
|
|
336
|
+
],
|
|
337
|
+
payable: false,
|
|
338
|
+
stateMutability: "nonpayable",
|
|
339
|
+
type: "function"
|
|
340
|
+
}
|
|
341
|
+
];
|
|
342
|
+
var ERC20_ALLOWANCE_ABI = [
|
|
343
|
+
{
|
|
344
|
+
constant: true,
|
|
345
|
+
inputs: [
|
|
346
|
+
{
|
|
347
|
+
name: "_owner",
|
|
348
|
+
type: "address"
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
name: "_spender",
|
|
352
|
+
type: "address"
|
|
353
|
+
}
|
|
354
|
+
],
|
|
355
|
+
name: "allowance",
|
|
356
|
+
outputs: [
|
|
357
|
+
{
|
|
358
|
+
name: "",
|
|
359
|
+
type: "uint256"
|
|
360
|
+
}
|
|
361
|
+
],
|
|
362
|
+
payable: false,
|
|
363
|
+
stateMutability: "view",
|
|
364
|
+
type: "function"
|
|
365
|
+
}
|
|
366
|
+
];
|
|
367
|
+
var AdapterContext = class _AdapterContext {
|
|
368
|
+
static _instance;
|
|
369
|
+
_adapter;
|
|
370
|
+
constructor() {}
|
|
371
|
+
static getInstance() {
|
|
372
|
+
if (!_AdapterContext._instance) {
|
|
373
|
+
_AdapterContext._instance = new _AdapterContext;
|
|
374
|
+
}
|
|
375
|
+
return _AdapterContext._instance;
|
|
376
|
+
}
|
|
377
|
+
setAdapter(adapter) {
|
|
378
|
+
this._adapter = adapter;
|
|
379
|
+
}
|
|
380
|
+
getAdapter() {
|
|
381
|
+
if (!this._adapter) {
|
|
382
|
+
throw new Error("Provider adapter is not configurated. Configure with CowSdk or using AdapterContext.getInstance().setAdapter()");
|
|
383
|
+
}
|
|
384
|
+
return this._adapter;
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
function getGlobalAdapter() {
|
|
388
|
+
return AdapterContext.getInstance().getAdapter();
|
|
389
|
+
}
|
|
390
|
+
function setGlobalAdapter(adapter) {
|
|
391
|
+
const instance = AdapterContext.getInstance();
|
|
392
|
+
instance.setAdapter(adapter);
|
|
393
|
+
}
|
|
394
|
+
var ContractFactory = class {
|
|
395
|
+
static createEthFlowContract(address, signer) {
|
|
396
|
+
const adapter = getGlobalAdapter();
|
|
397
|
+
return {
|
|
398
|
+
address,
|
|
399
|
+
estimateGas: {
|
|
400
|
+
createOrder: async (order, options) => {
|
|
401
|
+
const txParams = {
|
|
402
|
+
to: address,
|
|
403
|
+
data: adapter.utils.encodeFunction(EthFlowAbi, "createOrder", [order]),
|
|
404
|
+
value: options?.value || "0"
|
|
405
|
+
};
|
|
406
|
+
return await signer.estimateGas(txParams);
|
|
407
|
+
},
|
|
408
|
+
invalidateOrder: async (order) => {
|
|
409
|
+
const txParams = {
|
|
410
|
+
to: address,
|
|
411
|
+
data: adapter.utils.encodeFunction(EthFlowAbi, "invalidateOrder", [order])
|
|
412
|
+
};
|
|
413
|
+
return await signer.estimateGas(txParams);
|
|
414
|
+
},
|
|
415
|
+
orders: async (orderHash) => {
|
|
416
|
+
const txParams = {
|
|
417
|
+
to: address,
|
|
418
|
+
data: adapter.utils.encodeFunction(EthFlowAbi, "orders", [orderHash])
|
|
419
|
+
};
|
|
420
|
+
return await signer.estimateGas(txParams);
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
interface: {
|
|
424
|
+
encodeFunctionData: (functionName, args) => {
|
|
425
|
+
return adapter.utils.encodeFunction(EthFlowAbi, functionName, args);
|
|
426
|
+
},
|
|
427
|
+
decodeFunctionData: (functionName, data) => {
|
|
428
|
+
return adapter.utils.decodeFunctionData(EthFlowAbi, functionName, data);
|
|
429
|
+
},
|
|
430
|
+
parseLog(event) {
|
|
431
|
+
return adapter.utils.createInterface(EthFlowAbi).parseLog(event);
|
|
432
|
+
},
|
|
433
|
+
getEventTopic(eventFragment) {
|
|
434
|
+
return adapter.utils.createInterface(EthFlowAbi).getEventTopic(eventFragment);
|
|
435
|
+
}
|
|
436
|
+
},
|
|
437
|
+
functions: {
|
|
438
|
+
createOrder: async (order, options) => {
|
|
439
|
+
const txParams = {
|
|
440
|
+
to: address,
|
|
441
|
+
data: adapter.utils.encodeFunction(EthFlowAbi, "createOrder", [order]),
|
|
442
|
+
value: options?.value || "0"
|
|
443
|
+
};
|
|
444
|
+
return await signer.sendTransaction(txParams);
|
|
445
|
+
},
|
|
446
|
+
invalidateOrder: async (order) => {
|
|
447
|
+
const txParams = {
|
|
448
|
+
to: address,
|
|
449
|
+
data: adapter.utils.encodeFunction(EthFlowAbi, "invalidateOrder", [order])
|
|
450
|
+
};
|
|
451
|
+
return await signer.sendTransaction(txParams);
|
|
452
|
+
},
|
|
453
|
+
orders: async (orderHash) => {
|
|
454
|
+
return await adapter.readContract({
|
|
455
|
+
address,
|
|
456
|
+
abi: EthFlowAbi,
|
|
457
|
+
functionName: "orders",
|
|
458
|
+
args: [orderHash]
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
},
|
|
462
|
+
createOrder: async (order, options) => {
|
|
463
|
+
const txParams = {
|
|
464
|
+
to: address,
|
|
465
|
+
data: adapter.utils.encodeFunction(EthFlowAbi, "createOrder", [order]),
|
|
466
|
+
value: options?.value || "0"
|
|
467
|
+
};
|
|
468
|
+
return await signer.sendTransaction(txParams);
|
|
469
|
+
},
|
|
470
|
+
invalidateOrder: async (order) => {
|
|
471
|
+
const txParams = {
|
|
472
|
+
to: address,
|
|
473
|
+
data: adapter.utils.encodeFunction(EthFlowAbi, "invalidateOrder", [order])
|
|
474
|
+
};
|
|
475
|
+
return await signer.sendTransaction(txParams);
|
|
476
|
+
},
|
|
477
|
+
orders: async (orderHash) => {
|
|
478
|
+
return await adapter.readContract({
|
|
479
|
+
address,
|
|
480
|
+
abi: EthFlowAbi,
|
|
481
|
+
functionName: "orders",
|
|
482
|
+
args: [orderHash]
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
static createSettlementContract(address, signer) {
|
|
488
|
+
const adapter = getGlobalAdapter();
|
|
489
|
+
return {
|
|
490
|
+
address,
|
|
491
|
+
estimateGas: {
|
|
492
|
+
setPreSignature: async (orderUid, signed) => {
|
|
493
|
+
const txParams = {
|
|
494
|
+
to: address,
|
|
495
|
+
data: adapter.utils.encodeFunction(GPV2SettlementAbi, "setPreSignature", [orderUid, signed])
|
|
496
|
+
};
|
|
497
|
+
return await signer.estimateGas(txParams);
|
|
498
|
+
},
|
|
499
|
+
invalidateOrder: async (orderUid) => {
|
|
500
|
+
const txParams = {
|
|
501
|
+
to: address,
|
|
502
|
+
data: adapter.utils.encodeFunction(GPV2SettlementAbi, "invalidateOrder", [orderUid])
|
|
503
|
+
};
|
|
504
|
+
return await signer.estimateGas(txParams);
|
|
505
|
+
},
|
|
506
|
+
domainSeparator: async () => {
|
|
507
|
+
const txParams = {
|
|
508
|
+
to: address,
|
|
509
|
+
data: adapter.utils.encodeFunction(GPV2SettlementAbi, "domainSeparator", [])
|
|
510
|
+
};
|
|
511
|
+
return await signer.estimateGas(txParams);
|
|
512
|
+
}
|
|
513
|
+
},
|
|
514
|
+
interface: {
|
|
515
|
+
encodeFunctionData: (functionName, args) => {
|
|
516
|
+
return adapter.utils.encodeFunction(GPV2SettlementAbi, functionName, args);
|
|
517
|
+
},
|
|
518
|
+
decodeFunctionData: (functionName, data) => {
|
|
519
|
+
return adapter.utils.decodeFunctionData(GPV2SettlementAbi, functionName, data);
|
|
520
|
+
},
|
|
521
|
+
parseLog(event) {
|
|
522
|
+
return adapter.utils.createInterface(GPV2SettlementAbi).parseLog(event);
|
|
523
|
+
},
|
|
524
|
+
getEventTopic(eventFragment) {
|
|
525
|
+
return adapter.utils.createInterface(GPV2SettlementAbi).getEventTopic(eventFragment);
|
|
526
|
+
}
|
|
527
|
+
},
|
|
528
|
+
functions: {
|
|
529
|
+
setPreSignature: async (orderUid, signed) => {
|
|
530
|
+
const txParams = {
|
|
531
|
+
to: address,
|
|
532
|
+
data: adapter.utils.encodeFunction(GPV2SettlementAbi, "setPreSignature", [orderUid, signed])
|
|
533
|
+
};
|
|
534
|
+
return await signer.sendTransaction(txParams);
|
|
535
|
+
},
|
|
536
|
+
invalidateOrder: async (orderUid) => {
|
|
537
|
+
const txParams = {
|
|
538
|
+
to: address,
|
|
539
|
+
data: adapter.utils.encodeFunction(GPV2SettlementAbi, "invalidateOrder", [orderUid])
|
|
540
|
+
};
|
|
541
|
+
return await signer.sendTransaction(txParams);
|
|
542
|
+
},
|
|
543
|
+
domainSeparator: async () => {
|
|
544
|
+
return await adapter.readContract({
|
|
545
|
+
address,
|
|
546
|
+
abi: GPV2SettlementAbi,
|
|
547
|
+
functionName: "domainSeparator",
|
|
548
|
+
args: []
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
},
|
|
552
|
+
setPreSignature: async (orderUid, signed) => {
|
|
553
|
+
const txParams = {
|
|
554
|
+
to: address,
|
|
555
|
+
data: adapter.utils.encodeFunction(GPV2SettlementAbi, "setPreSignature", [orderUid, signed])
|
|
556
|
+
};
|
|
557
|
+
return await signer.sendTransaction(txParams);
|
|
558
|
+
},
|
|
559
|
+
invalidateOrder: async (orderUid) => {
|
|
560
|
+
const txParams = {
|
|
561
|
+
to: address,
|
|
562
|
+
data: adapter.utils.encodeFunction(GPV2SettlementAbi, "invalidateOrder", [orderUid])
|
|
563
|
+
};
|
|
564
|
+
return await signer.sendTransaction(txParams);
|
|
565
|
+
},
|
|
566
|
+
domainSeparator: async () => {
|
|
567
|
+
return await adapter.readContract({
|
|
568
|
+
address,
|
|
569
|
+
abi: GPV2SettlementAbi,
|
|
570
|
+
functionName: "domainSeparator",
|
|
571
|
+
args: []
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
var AbstractSigner = class {
|
|
578
|
+
};
|
|
579
|
+
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
580
|
+
var ZERO = BigInt(0);
|
|
581
|
+
var ONE = BigInt(1);
|
|
582
|
+
var ZERO_HASH = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
583
|
+
var MAX_UINT32 = BigInt(2) ** BigInt(32) - BigInt(1);
|
|
584
|
+
var CowError = class extends Error {
|
|
585
|
+
error_code;
|
|
586
|
+
constructor(message, error_code) {
|
|
587
|
+
super(message);
|
|
588
|
+
this.error_code = error_code;
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
var logEnabled = false;
|
|
592
|
+
function log(text) {
|
|
593
|
+
if (!logEnabled)
|
|
594
|
+
return;
|
|
595
|
+
console.log(`[COW SDK] ${text}`);
|
|
596
|
+
}
|
|
597
|
+
function enableLogging(enabled) {
|
|
598
|
+
logEnabled = enabled;
|
|
599
|
+
}
|
|
600
|
+
var jsonWithBigintReplacer = (_key, value) => {
|
|
601
|
+
if (typeof value === "bigint") {
|
|
602
|
+
return value.toString();
|
|
603
|
+
}
|
|
604
|
+
if (typeof value === "object" && value !== null && "_isBigNumber" in value) {
|
|
605
|
+
return value.toString();
|
|
606
|
+
}
|
|
607
|
+
return value;
|
|
608
|
+
};
|
|
609
|
+
var SCALE = 1e6;
|
|
610
|
+
var SCALE_BIGINT = BigInt(SCALE);
|
|
611
|
+
var BPS_FACTOR = 10000n;
|
|
612
|
+
function percentageToBps(percentage) {
|
|
613
|
+
const bps = typeof percentage === "bigint" ? Number(percentage * BPS_FACTOR) : percentage * Number(BPS_FACTOR);
|
|
614
|
+
return Math.round(bps);
|
|
615
|
+
}
|
|
616
|
+
function bpsToPercentage(bps) {
|
|
617
|
+
return bps / 100;
|
|
618
|
+
}
|
|
619
|
+
function applyPercentage(value, percentage) {
|
|
620
|
+
const valueMultiplied = value * BigInt(Math.floor(percentage * SCALE)) / SCALE_BIGINT;
|
|
621
|
+
const roundUp = valueMultiplied % 100n >= 50n ? 1n : 0n;
|
|
622
|
+
return valueMultiplied / 100n + roundUp;
|
|
623
|
+
}
|
|
624
|
+
function normalizePrivateKey(privateKey) {
|
|
625
|
+
if (!privateKey || typeof privateKey !== "string") {
|
|
626
|
+
throw new CowError("Private key must be a non-empty string");
|
|
627
|
+
}
|
|
628
|
+
const cleanKey = privateKey.startsWith("0x") ? privateKey.slice(2) : privateKey;
|
|
629
|
+
if (!/^[a-fA-F0-9]{64}$/.test(cleanKey)) {
|
|
630
|
+
throw new CowError("Invalid private key format: must be exactly 64 hexadecimal characters (with or without 0x prefix)");
|
|
631
|
+
}
|
|
632
|
+
return `0x${cleanKey}`;
|
|
633
|
+
}
|
|
634
|
+
function isValidPrivateKey(privateKey) {
|
|
635
|
+
try {
|
|
636
|
+
normalizePrivateKey(privateKey);
|
|
637
|
+
return true;
|
|
638
|
+
} catch {
|
|
639
|
+
return false;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
var MemoryStorage = class {
|
|
643
|
+
storage = /* @__PURE__ */ new Map;
|
|
644
|
+
getItem(key) {
|
|
645
|
+
return this.storage.get(key) ?? null;
|
|
646
|
+
}
|
|
647
|
+
setItem(key, value) {
|
|
648
|
+
this.storage.set(key, value);
|
|
649
|
+
}
|
|
650
|
+
removeItem(key) {
|
|
651
|
+
this.storage.delete(key);
|
|
652
|
+
}
|
|
653
|
+
size(prefix) {
|
|
654
|
+
if (!prefix)
|
|
655
|
+
return this.storage.size;
|
|
656
|
+
let count = 0;
|
|
657
|
+
for (const key of this.storage.keys()) {
|
|
658
|
+
if (key.startsWith(prefix))
|
|
659
|
+
count++;
|
|
660
|
+
}
|
|
661
|
+
return count;
|
|
662
|
+
}
|
|
663
|
+
clear(prefix) {
|
|
664
|
+
if (!prefix) {
|
|
665
|
+
this.storage.clear();
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
const keysToDelete = [];
|
|
669
|
+
for (const key of this.storage.keys()) {
|
|
670
|
+
if (key.startsWith(prefix))
|
|
671
|
+
keysToDelete.push(key);
|
|
672
|
+
}
|
|
673
|
+
keysToDelete.forEach((key) => this.storage.delete(key));
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
var LocalStorageWrapper = class {
|
|
677
|
+
fallback = new MemoryStorage;
|
|
678
|
+
getItem(key) {
|
|
679
|
+
try {
|
|
680
|
+
return typeof localStorage !== "undefined" ? localStorage.getItem(key) : this.fallback.getItem(key);
|
|
681
|
+
} catch {
|
|
682
|
+
return this.fallback.getItem(key);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
setItem(key, value) {
|
|
686
|
+
try {
|
|
687
|
+
if (typeof localStorage !== "undefined") {
|
|
688
|
+
localStorage.setItem(key, value);
|
|
689
|
+
} else {
|
|
690
|
+
this.fallback.setItem(key, value);
|
|
691
|
+
}
|
|
692
|
+
} catch {
|
|
693
|
+
this.fallback.setItem(key, value);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
removeItem(key) {
|
|
697
|
+
try {
|
|
698
|
+
if (typeof localStorage !== "undefined") {
|
|
699
|
+
localStorage.removeItem(key);
|
|
700
|
+
} else {
|
|
701
|
+
this.fallback.removeItem(key);
|
|
702
|
+
}
|
|
703
|
+
} catch {
|
|
704
|
+
this.fallback.removeItem(key);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
var DEFAULT_KEY_PREFIX = "ttl-cache";
|
|
709
|
+
var DEFAULT_TTL = 2 * 60 * 1000;
|
|
710
|
+
var TTLCache = class {
|
|
711
|
+
storage;
|
|
712
|
+
keyPrefix;
|
|
713
|
+
ttl;
|
|
714
|
+
keys = /* @__PURE__ */ new Set;
|
|
715
|
+
get isMemoryStorage() {
|
|
716
|
+
return this.storage instanceof MemoryStorage;
|
|
717
|
+
}
|
|
718
|
+
constructor(keyPrefix = DEFAULT_KEY_PREFIX, useLocalStorage = true, ttl = DEFAULT_TTL) {
|
|
719
|
+
this.keyPrefix = keyPrefix;
|
|
720
|
+
this.ttl = ttl;
|
|
721
|
+
this.storage = useLocalStorage ? new LocalStorageWrapper : new MemoryStorage;
|
|
722
|
+
if (useLocalStorage && typeof localStorage === "undefined") {
|
|
723
|
+
this.storage = new MemoryStorage;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
set(key, value) {
|
|
727
|
+
if (this.ttl <= 0)
|
|
728
|
+
return;
|
|
729
|
+
const entry = {
|
|
730
|
+
value,
|
|
731
|
+
timestamp: Date.now(),
|
|
732
|
+
ttl: this.ttl
|
|
733
|
+
};
|
|
734
|
+
const storageKey = this.getStorageKey(key);
|
|
735
|
+
try {
|
|
736
|
+
this.storage.setItem(storageKey, JSON.stringify(entry));
|
|
737
|
+
} catch (error) {
|
|
738
|
+
console.warn("TTLCache: Failed to store cache entry", error);
|
|
739
|
+
}
|
|
740
|
+
this.keys.add(storageKey);
|
|
741
|
+
}
|
|
742
|
+
get(key) {
|
|
743
|
+
const storageKey = this.getStorageKey(key);
|
|
744
|
+
const item = this.storage.getItem(storageKey);
|
|
745
|
+
if (!item) {
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
try {
|
|
749
|
+
const entry = JSON.parse(item);
|
|
750
|
+
const now = Date.now();
|
|
751
|
+
if (now - entry.timestamp > entry.ttl) {
|
|
752
|
+
this.storage.removeItem(storageKey);
|
|
753
|
+
this.keys.delete(storageKey);
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
return entry.value;
|
|
757
|
+
} catch (error) {
|
|
758
|
+
console.warn("TTLCache: Failed to parse cache entry", error);
|
|
759
|
+
this.storage.removeItem(storageKey);
|
|
760
|
+
this.keys.delete(storageKey);
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
delete(key) {
|
|
765
|
+
const storageKey = this.getStorageKey(key);
|
|
766
|
+
this.storage.removeItem(storageKey);
|
|
767
|
+
this.keys.delete(storageKey);
|
|
768
|
+
}
|
|
769
|
+
clear() {
|
|
770
|
+
const prefix = this.keyPrefix + ":";
|
|
771
|
+
if (this.isMemoryStorage) {
|
|
772
|
+
this.storage.clear(prefix);
|
|
773
|
+
this.keys.clear();
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
if (typeof localStorage !== "undefined") {
|
|
777
|
+
try {
|
|
778
|
+
const keysToDelete = [];
|
|
779
|
+
for (let i = 0;i < localStorage.length; i++) {
|
|
780
|
+
const key = localStorage.key(i);
|
|
781
|
+
if (key && key.startsWith(prefix)) {
|
|
782
|
+
keysToDelete.push(key);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
keysToDelete.forEach((key) => localStorage.removeItem(key));
|
|
786
|
+
} catch {
|
|
787
|
+
console.warn("TTLCache: Failed to clear localStorage cache");
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
for (const key of Array.from(this.keys)) {
|
|
791
|
+
try {
|
|
792
|
+
this.storage.removeItem(key);
|
|
793
|
+
} catch (error) {
|
|
794
|
+
console.warn("TTLCache: Failed to remove cache key", error);
|
|
795
|
+
}
|
|
796
|
+
this.keys.delete(key);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
size() {
|
|
800
|
+
const prefix = this.keyPrefix + ":";
|
|
801
|
+
if (this.isMemoryStorage && this.storage instanceof MemoryStorage) {
|
|
802
|
+
return this.storage.size(prefix);
|
|
803
|
+
}
|
|
804
|
+
if (typeof localStorage === "undefined") {
|
|
805
|
+
return this.keys.size;
|
|
806
|
+
}
|
|
807
|
+
if (typeof localStorage !== "undefined") {
|
|
808
|
+
try {
|
|
809
|
+
let count = 0;
|
|
810
|
+
for (let i = 0;i < localStorage.length; i++) {
|
|
811
|
+
const key = localStorage.key(i);
|
|
812
|
+
if (key && key.startsWith(prefix)) {
|
|
813
|
+
count++;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
return count;
|
|
817
|
+
} catch {
|
|
818
|
+
return this.keys.size;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
return this.keys.size;
|
|
822
|
+
}
|
|
823
|
+
cleanup() {
|
|
824
|
+
const prefix = this.keyPrefix + ":";
|
|
825
|
+
if (typeof localStorage !== "undefined") {
|
|
826
|
+
try {
|
|
827
|
+
const keysToDelete = [];
|
|
828
|
+
const now2 = Date.now();
|
|
829
|
+
for (let i = 0;i < localStorage.length; i++) {
|
|
830
|
+
const key = localStorage.key(i);
|
|
831
|
+
if (key && key.startsWith(prefix)) {
|
|
832
|
+
const item = localStorage.getItem(key);
|
|
833
|
+
if (item) {
|
|
834
|
+
try {
|
|
835
|
+
const entry = JSON.parse(item);
|
|
836
|
+
if (now2 - entry.timestamp > entry.ttl) {
|
|
837
|
+
keysToDelete.push(key);
|
|
838
|
+
}
|
|
839
|
+
} catch {
|
|
840
|
+
keysToDelete.push(key);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
keysToDelete.forEach((key) => localStorage.removeItem(key));
|
|
846
|
+
} catch {
|
|
847
|
+
console.warn("TTLCache: Failed to cleanup expired entries");
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
const now = Date.now();
|
|
851
|
+
for (const key of Array.from(this.keys)) {
|
|
852
|
+
const item = this.storage.getItem(key);
|
|
853
|
+
if (!item) {
|
|
854
|
+
this.keys.delete(key);
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
try {
|
|
858
|
+
const entry = JSON.parse(item);
|
|
859
|
+
if (now - entry.timestamp > entry.ttl) {
|
|
860
|
+
this.storage.removeItem(key);
|
|
861
|
+
this.keys.delete(key);
|
|
862
|
+
}
|
|
863
|
+
} catch {
|
|
864
|
+
this.storage.removeItem(key);
|
|
865
|
+
this.keys.delete(key);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
getStorageKey(key) {
|
|
870
|
+
return `${this.keyPrefix}:${key}`;
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
// src/rift-api-client.ts
|
|
876
|
+
var BTC = {
|
|
877
|
+
chain: { kind: "BITCOIN" },
|
|
878
|
+
token: { kind: "NATIVE", decimals: 8 }
|
|
879
|
+
};
|
|
880
|
+
var CBBTC_ETHEREUM = {
|
|
881
|
+
chain: { kind: "EVM", chainId: 1 },
|
|
882
|
+
token: {
|
|
883
|
+
kind: "TOKEN",
|
|
884
|
+
address: "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf",
|
|
885
|
+
decimals: 8
|
|
886
|
+
}
|
|
887
|
+
};
|
|
888
|
+
var CBBTC_BASE = {
|
|
889
|
+
chain: { kind: "EVM", chainId: 8453 },
|
|
890
|
+
token: {
|
|
891
|
+
kind: "TOKEN",
|
|
892
|
+
address: "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf",
|
|
893
|
+
decimals: 8
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
class SwapRouterApiError extends Error {
|
|
898
|
+
status;
|
|
899
|
+
body;
|
|
900
|
+
constructor(message, status, body) {
|
|
901
|
+
super(message);
|
|
902
|
+
this.name = "SwapRouterApiError";
|
|
903
|
+
this.status = status;
|
|
904
|
+
this.body = body;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
class SwapRouterClient {
|
|
909
|
+
baseUrl;
|
|
910
|
+
fetch;
|
|
911
|
+
constructor(options) {
|
|
912
|
+
this.baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
913
|
+
this.fetch = options.fetch ?? globalThis.fetch;
|
|
914
|
+
}
|
|
915
|
+
async request(method, path, body) {
|
|
916
|
+
const url = `${this.baseUrl}${path}`;
|
|
917
|
+
const response = await this.fetch(url, {
|
|
918
|
+
method,
|
|
919
|
+
headers: body ? { "Content-Type": "application/json" } : undefined,
|
|
920
|
+
body: body ? JSON.stringify(body) : undefined
|
|
921
|
+
});
|
|
922
|
+
const text = await response.text();
|
|
923
|
+
let data;
|
|
924
|
+
try {
|
|
925
|
+
data = JSON.parse(text);
|
|
926
|
+
} catch {
|
|
927
|
+
if (!response.ok) {
|
|
928
|
+
throw new SwapRouterApiError(text || `Request failed with status ${response.status}`, response.status);
|
|
929
|
+
}
|
|
930
|
+
throw new SwapRouterApiError(`Invalid JSON response: ${text}`, response.status);
|
|
931
|
+
}
|
|
932
|
+
if (!response.ok) {
|
|
933
|
+
throw new SwapRouterApiError(data.error ?? `Request failed with status ${response.status}`, response.status, data);
|
|
934
|
+
}
|
|
935
|
+
return data;
|
|
936
|
+
}
|
|
937
|
+
async health() {
|
|
938
|
+
return this.request("GET", "/health");
|
|
939
|
+
}
|
|
940
|
+
async status() {
|
|
941
|
+
return this.request("GET", "/status");
|
|
942
|
+
}
|
|
943
|
+
async quote(request) {
|
|
944
|
+
return this.request("POST", "/quote", request);
|
|
945
|
+
}
|
|
946
|
+
async createOrder(request) {
|
|
947
|
+
return this.request("POST", "/order", request);
|
|
948
|
+
}
|
|
949
|
+
async getOrder(orderId) {
|
|
950
|
+
return this.request("GET", `/order/${orderId}`);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
// src/router.ts
|
|
954
|
+
import { match, P } from "ts-pattern";
|
|
955
|
+
var CBBTC_ADDRESS = "0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf";
|
|
956
|
+
var isBitcoin = (currency) => currency.chain.kind === "BITCOIN";
|
|
957
|
+
var isCbBtc = (currency) => currency.chain.kind === "EVM" && currency.token.kind === "TOKEN" && currency.token.address.toLowerCase() === CBBTC_ADDRESS;
|
|
958
|
+
var isEvmToken = (currency) => currency.chain.kind === "EVM";
|
|
959
|
+
function detectRoute(from, to) {
|
|
960
|
+
return match({ from, to }).with({ from: P.when(isCbBtc), to: P.when(isBitcoin) }, () => ({
|
|
961
|
+
type: "direct_rift",
|
|
962
|
+
direction: "to_btc"
|
|
963
|
+
})).with({ from: P.when(isBitcoin), to: P.when(isEvmToken) }, () => ({
|
|
964
|
+
type: "direct_rift",
|
|
965
|
+
direction: "from_btc"
|
|
966
|
+
})).with({
|
|
967
|
+
from: P.when((c) => isEvmToken(c) && !isCbBtc(c)),
|
|
968
|
+
to: P.when(isBitcoin)
|
|
969
|
+
}, ({ from: from2 }) => {
|
|
970
|
+
if (from2.chain.kind !== "EVM") {
|
|
971
|
+
throw new Error("Expected EVM chain");
|
|
972
|
+
}
|
|
973
|
+
return {
|
|
974
|
+
type: "cowswap_then_rift",
|
|
975
|
+
evmChainId: from2.chain.chainId
|
|
976
|
+
};
|
|
977
|
+
}).otherwise(() => {
|
|
978
|
+
throw new Error("Invalid swap: one side must be BTC");
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
function getCbBtcAddress(chainId) {
|
|
982
|
+
return CBBTC_ADDRESS;
|
|
983
|
+
}
|
|
984
|
+
var NATIVE_TOKEN_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
|
|
985
|
+
function getTokenAddress(currency) {
|
|
986
|
+
if (currency.token.kind === "NATIVE") {
|
|
987
|
+
return NATIVE_TOKEN_ADDRESS;
|
|
988
|
+
}
|
|
989
|
+
return currency.token.address;
|
|
990
|
+
}
|
|
991
|
+
function isNativeToken(currency) {
|
|
992
|
+
return currency.token.kind === "NATIVE";
|
|
993
|
+
}
|
|
994
|
+
function getTokenDecimals(currency) {
|
|
995
|
+
return currency.token.decimals;
|
|
996
|
+
}
|
|
997
|
+
function getSupportedModes(from, to) {
|
|
998
|
+
const route = detectRoute(from, to);
|
|
999
|
+
if (route.type === "cowswap_then_rift") {
|
|
1000
|
+
return { exactInput: true, exactOutput: true };
|
|
1001
|
+
}
|
|
1002
|
+
if (route.direction === "to_btc") {
|
|
1003
|
+
return { exactInput: true, exactOutput: true };
|
|
1004
|
+
}
|
|
1005
|
+
if (isCbBtc(to)) {
|
|
1006
|
+
return { exactInput: true, exactOutput: true };
|
|
1007
|
+
}
|
|
1008
|
+
return { exactInput: true, exactOutput: false };
|
|
1009
|
+
}
|
|
1010
|
+
// src/sdk.ts
|
|
1011
|
+
import { match as match2 } from "ts-pattern";
|
|
1012
|
+
import { erc20Abi } from "viem";
|
|
1013
|
+
|
|
1014
|
+
// src/cowswap.ts
|
|
1015
|
+
var import_sdk_common = __toESM(require_dist(), 1);
|
|
1016
|
+
import { OrderKind } from "@cowprotocol/sdk-order-book";
|
|
1017
|
+
import { TradingSdk } from "@cowprotocol/sdk-trading";
|
|
1018
|
+
import { ViemAdapter } from "@cowprotocol/sdk-viem-adapter";
|
|
1019
|
+
function createCowSwapSdk(chainId, publicClient, walletClient) {
|
|
1020
|
+
const adapter = new ViemAdapter({
|
|
1021
|
+
provider: publicClient,
|
|
1022
|
+
walletClient
|
|
1023
|
+
});
|
|
1024
|
+
import_sdk_common.setGlobalAdapter(adapter);
|
|
1025
|
+
return new TradingSdk({
|
|
1026
|
+
chainId,
|
|
1027
|
+
appCode: "rift.trade"
|
|
1028
|
+
}, {}, adapter);
|
|
1029
|
+
}
|
|
1030
|
+
var MAX_UINT256 = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn;
|
|
1031
|
+
async function ensureCowSwapApproval(sdk, tokenAddress, ownerAddress, amount) {
|
|
1032
|
+
const currentAllowance = await sdk.getCowProtocolAllowance({
|
|
1033
|
+
tokenAddress,
|
|
1034
|
+
owner: ownerAddress
|
|
1035
|
+
});
|
|
1036
|
+
if (currentAllowance >= BigInt(amount)) {
|
|
1037
|
+
return false;
|
|
1038
|
+
}
|
|
1039
|
+
await sdk.approveCowProtocol({
|
|
1040
|
+
tokenAddress,
|
|
1041
|
+
amount: MAX_UINT256
|
|
1042
|
+
});
|
|
1043
|
+
return true;
|
|
1044
|
+
}
|
|
1045
|
+
async function getCowSwapQuote(sdk, params) {
|
|
1046
|
+
const { quoteResults, postSwapOrderFromQuote } = await sdk.getQuote({
|
|
1047
|
+
kind: params.kind === "SELL" ? OrderKind.SELL : OrderKind.BUY,
|
|
1048
|
+
sellToken: params.sellToken,
|
|
1049
|
+
sellTokenDecimals: params.sellTokenDecimals,
|
|
1050
|
+
buyToken: params.buyToken,
|
|
1051
|
+
buyTokenDecimals: params.buyTokenDecimals,
|
|
1052
|
+
amount: params.amount,
|
|
1053
|
+
receiver: params.receiver,
|
|
1054
|
+
slippageBps: params.slippageBps
|
|
1055
|
+
});
|
|
1056
|
+
const validTo = quoteResults.quoteResponse.quote.validTo;
|
|
1057
|
+
const expiresAt = new Date(validTo * 1000);
|
|
1058
|
+
const quote = {
|
|
1059
|
+
quoteId: quoteResults.quoteResponse.id?.toString() ?? "",
|
|
1060
|
+
buyAmount: quoteResults.amountsAndCosts.afterSlippage.buyAmount.toString(),
|
|
1061
|
+
sellAmount: quoteResults.amountsAndCosts.afterSlippage.sellAmount.toString(),
|
|
1062
|
+
feeAmount: quoteResults.amountsAndCosts.costs.networkFee.amountInSellCurrency.toString(),
|
|
1063
|
+
expiresAt
|
|
1064
|
+
};
|
|
1065
|
+
return {
|
|
1066
|
+
quote,
|
|
1067
|
+
postOrder: async () => {
|
|
1068
|
+
const { orderId } = await postSwapOrderFromQuote();
|
|
1069
|
+
return orderId;
|
|
1070
|
+
}
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// src/swap-id.ts
|
|
1075
|
+
function encodeSwapId(data) {
|
|
1076
|
+
let raw;
|
|
1077
|
+
if (data.type === "rift") {
|
|
1078
|
+
raw = data.chained ? `r|${data.riftOrderId}|c` : `r|${data.riftOrderId}`;
|
|
1079
|
+
} else {
|
|
1080
|
+
raw = `c|${data.cowOrderId}|${data.riftOrderId}`;
|
|
1081
|
+
}
|
|
1082
|
+
return toBase64Url(raw);
|
|
1083
|
+
}
|
|
1084
|
+
function decodeSwapId(encoded) {
|
|
1085
|
+
const raw = fromBase64Url(encoded);
|
|
1086
|
+
const parts = raw.split("|");
|
|
1087
|
+
if (parts[0] === "r" && (parts.length === 2 || parts.length === 3)) {
|
|
1088
|
+
return {
|
|
1089
|
+
type: "rift",
|
|
1090
|
+
riftOrderId: parts[1],
|
|
1091
|
+
chained: parts[2] === "c"
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
if (parts[0] === "c" && parts.length === 3) {
|
|
1095
|
+
return { type: "cowswap_rift", cowOrderId: parts[1], riftOrderId: parts[2] };
|
|
1096
|
+
}
|
|
1097
|
+
throw new Error(`Invalid swap ID format: ${encoded}`);
|
|
1098
|
+
}
|
|
1099
|
+
function toBase64Url(str) {
|
|
1100
|
+
return Buffer.from(str, "utf-8").toString("base64url");
|
|
1101
|
+
}
|
|
1102
|
+
function fromBase64Url(str) {
|
|
1103
|
+
return Buffer.from(str, "base64url").toString("utf-8");
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
// src/sdk.ts
|
|
1107
|
+
class RiftSdk {
|
|
1108
|
+
riftClient;
|
|
1109
|
+
publicClient;
|
|
1110
|
+
walletClient;
|
|
1111
|
+
sendBitcoinFn;
|
|
1112
|
+
constructor(options) {
|
|
1113
|
+
this.riftClient = new SwapRouterClient({
|
|
1114
|
+
baseUrl: options.apiUrl ?? "https://api.rift.trade"
|
|
1115
|
+
});
|
|
1116
|
+
this.publicClient = options.publicClient;
|
|
1117
|
+
this.walletClient = options.walletClient;
|
|
1118
|
+
this.sendBitcoinFn = options.sendBitcoin;
|
|
1119
|
+
}
|
|
1120
|
+
async getQuote(params) {
|
|
1121
|
+
const route = detectRoute(params.from, params.to);
|
|
1122
|
+
return match2(route).with({ type: "direct_rift", direction: "to_btc" }, () => this.getDirectRiftQuote(params, "to_btc")).with({ type: "direct_rift", direction: "from_btc" }, () => this.getDirectRiftQuote(params, "from_btc")).with({ type: "cowswap_then_rift" }, (r) => this.getCowswapThenRiftQuote(params, r.evmChainId)).exhaustive();
|
|
1123
|
+
}
|
|
1124
|
+
async getDirectRiftQuote(params, direction) {
|
|
1125
|
+
const quoteRequest = {
|
|
1126
|
+
type: params.mode === "exact_input" ? "EXACT_INPUT" : "EXACT_OUTPUT",
|
|
1127
|
+
from: params.from,
|
|
1128
|
+
to: params.to,
|
|
1129
|
+
amount: params.amount
|
|
1130
|
+
};
|
|
1131
|
+
const riftQuote = await this.riftClient.quote(quoteRequest);
|
|
1132
|
+
const quote = this.buildQuoteResult(riftQuote, params);
|
|
1133
|
+
const isChained = direction === "from_btc" && !isCbBtc(params.to);
|
|
1134
|
+
return {
|
|
1135
|
+
quote,
|
|
1136
|
+
executeSwap: async () => {
|
|
1137
|
+
const refundAddress = params.refundAddress ?? await this.getRefundAddress(params);
|
|
1138
|
+
const swap = await this.riftClient.createOrder({
|
|
1139
|
+
id: riftQuote.id,
|
|
1140
|
+
userDestinationAddress: params.destinationAddress,
|
|
1141
|
+
refundAddress
|
|
1142
|
+
});
|
|
1143
|
+
await this.executeDeposit(swap, params, direction);
|
|
1144
|
+
return this.buildSwapResult(swap, { chained: isChained });
|
|
1145
|
+
}
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
async getCowswapThenRiftQuote(params, evmChainId) {
|
|
1149
|
+
const cbBtcAddress = getCbBtcAddress(evmChainId);
|
|
1150
|
+
const cbBtcCurrency = {
|
|
1151
|
+
chain: { kind: "EVM", chainId: evmChainId },
|
|
1152
|
+
token: { kind: "TOKEN", address: cbBtcAddress, decimals: 8 }
|
|
1153
|
+
};
|
|
1154
|
+
if (params.mode === "exact_input") {
|
|
1155
|
+
return this.getCowswapThenRiftExactInput(params, evmChainId, cbBtcAddress, cbBtcCurrency);
|
|
1156
|
+
} else {
|
|
1157
|
+
return this.getCowswapThenRiftExactOutput(params, evmChainId, cbBtcAddress, cbBtcCurrency);
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
async getCowswapThenRiftExactInput(params, evmChainId, cbBtcAddress, cbBtcCurrency) {
|
|
1161
|
+
const cowSdk = createCowSwapSdk(evmChainId, this.publicClient, this.walletClient);
|
|
1162
|
+
const sellToken = getTokenAddress(params.from);
|
|
1163
|
+
const { quote: cowQuote } = await getCowSwapQuote(cowSdk, {
|
|
1164
|
+
sellToken,
|
|
1165
|
+
buyToken: cbBtcAddress,
|
|
1166
|
+
amount: params.amount,
|
|
1167
|
+
sellTokenDecimals: getTokenDecimals(params.from),
|
|
1168
|
+
buyTokenDecimals: 8,
|
|
1169
|
+
kind: "SELL",
|
|
1170
|
+
slippageBps: params.preswapSlippageBps
|
|
1171
|
+
});
|
|
1172
|
+
const riftQuote = await this.riftClient.quote({
|
|
1173
|
+
type: "EXACT_INPUT",
|
|
1174
|
+
from: cbBtcCurrency,
|
|
1175
|
+
to: BTC,
|
|
1176
|
+
amount: cowQuote.buyAmount
|
|
1177
|
+
});
|
|
1178
|
+
const quote = this.buildCombinedQuoteResult(params, cowQuote, riftQuote);
|
|
1179
|
+
return {
|
|
1180
|
+
quote,
|
|
1181
|
+
executeSwap: () => this.executeCowswapThenRift(params, riftQuote, evmChainId, cbBtcAddress, sellToken)
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
async getCowswapThenRiftExactOutput(params, evmChainId, cbBtcAddress, cbBtcCurrency) {
|
|
1185
|
+
const cowSdk = createCowSwapSdk(evmChainId, this.publicClient, this.walletClient);
|
|
1186
|
+
const sellToken = getTokenAddress(params.from);
|
|
1187
|
+
const riftQuote = await this.riftClient.quote({
|
|
1188
|
+
type: "EXACT_OUTPUT",
|
|
1189
|
+
from: cbBtcCurrency,
|
|
1190
|
+
to: BTC,
|
|
1191
|
+
amount: params.amount
|
|
1192
|
+
});
|
|
1193
|
+
const { quote: cowQuote } = await getCowSwapQuote(cowSdk, {
|
|
1194
|
+
sellToken,
|
|
1195
|
+
buyToken: cbBtcAddress,
|
|
1196
|
+
amount: riftQuote.from.amount,
|
|
1197
|
+
sellTokenDecimals: getTokenDecimals(params.from),
|
|
1198
|
+
buyTokenDecimals: 8,
|
|
1199
|
+
kind: "BUY",
|
|
1200
|
+
slippageBps: params.preswapSlippageBps
|
|
1201
|
+
});
|
|
1202
|
+
const quote = this.buildCombinedQuoteResult(params, cowQuote, riftQuote);
|
|
1203
|
+
return {
|
|
1204
|
+
quote,
|
|
1205
|
+
executeSwap: () => this.executeCowswapThenRift(params, riftQuote, evmChainId, cbBtcAddress, sellToken)
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
1208
|
+
async executeCowswapThenRift(params, riftQuote, evmChainId, cbBtcAddress, sellToken) {
|
|
1209
|
+
const refundAddress = params.refundAddress ?? await this.getRefundAddress(params);
|
|
1210
|
+
const swap = await this.riftClient.createOrder({
|
|
1211
|
+
id: riftQuote.id,
|
|
1212
|
+
userDestinationAddress: params.destinationAddress,
|
|
1213
|
+
refundAddress
|
|
1214
|
+
});
|
|
1215
|
+
const cowSdk = createCowSwapSdk(evmChainId, this.publicClient, this.walletClient);
|
|
1216
|
+
const sellAmount = params.mode === "exact_input" ? params.amount : riftQuote.from.amount;
|
|
1217
|
+
if (!isNativeToken(params.from)) {
|
|
1218
|
+
const ownerAddress = await this.getAddress();
|
|
1219
|
+
await ensureCowSwapApproval(cowSdk, sellToken, ownerAddress, sellAmount);
|
|
1220
|
+
}
|
|
1221
|
+
const { postOrder } = await getCowSwapQuote(cowSdk, {
|
|
1222
|
+
sellToken,
|
|
1223
|
+
buyToken: cbBtcAddress,
|
|
1224
|
+
amount: sellAmount,
|
|
1225
|
+
sellTokenDecimals: getTokenDecimals(params.from),
|
|
1226
|
+
buyTokenDecimals: 8,
|
|
1227
|
+
kind: params.mode === "exact_input" ? "SELL" : "BUY",
|
|
1228
|
+
receiver: swap.deposit_vault_address,
|
|
1229
|
+
slippageBps: params.preswapSlippageBps
|
|
1230
|
+
});
|
|
1231
|
+
const cowOrderId = await postOrder();
|
|
1232
|
+
return this.buildSwapResult(swap, { cowOrderId });
|
|
1233
|
+
}
|
|
1234
|
+
buildQuoteResult(riftQuote, params) {
|
|
1235
|
+
return {
|
|
1236
|
+
quoteId: riftQuote.id,
|
|
1237
|
+
from: {
|
|
1238
|
+
currency: params.from,
|
|
1239
|
+
amount: riftQuote.from.amount
|
|
1240
|
+
},
|
|
1241
|
+
to: {
|
|
1242
|
+
currency: params.to,
|
|
1243
|
+
amount: riftQuote.to.amount
|
|
1244
|
+
},
|
|
1245
|
+
fees: {
|
|
1246
|
+
protocol: riftQuote.fees.raw.protocolFeeBps.toString(),
|
|
1247
|
+
liquidity: riftQuote.fees.raw.liquidityFeeBps.toString(),
|
|
1248
|
+
network: riftQuote.fees.raw.networkFeeSats,
|
|
1249
|
+
totalUsd: riftQuote.fees.usd.marketMaker + riftQuote.fees.usd.protocol + riftQuote.fees.usd.network
|
|
1250
|
+
},
|
|
1251
|
+
expiresAt: new Date(riftQuote.expiresAt)
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
buildCombinedQuoteResult(params, cowQuote, riftQuote) {
|
|
1255
|
+
const earliestExpiry = new Date(Math.min(cowQuote.expiresAt.getTime(), new Date(riftQuote.expiresAt).getTime()));
|
|
1256
|
+
const fromAmount = params.mode === "exact_input" ? params.amount : cowQuote.sellAmount;
|
|
1257
|
+
return {
|
|
1258
|
+
quoteId: `${cowQuote.quoteId}:${riftQuote.id}`,
|
|
1259
|
+
from: {
|
|
1260
|
+
currency: params.from,
|
|
1261
|
+
amount: fromAmount
|
|
1262
|
+
},
|
|
1263
|
+
to: {
|
|
1264
|
+
currency: params.to,
|
|
1265
|
+
amount: riftQuote.to.amount
|
|
1266
|
+
},
|
|
1267
|
+
fees: {
|
|
1268
|
+
protocol: riftQuote.fees.raw.protocolFeeBps.toString(),
|
|
1269
|
+
liquidity: riftQuote.fees.raw.liquidityFeeBps.toString(),
|
|
1270
|
+
network: riftQuote.fees.raw.networkFeeSats,
|
|
1271
|
+
totalUsd: riftQuote.fees.usd.marketMaker + riftQuote.fees.usd.protocol + riftQuote.fees.usd.network
|
|
1272
|
+
},
|
|
1273
|
+
expiresAt: earliestExpiry
|
|
1274
|
+
};
|
|
1275
|
+
}
|
|
1276
|
+
buildSwapResult(swap, options) {
|
|
1277
|
+
const { cowOrderId, preswap, chained } = options ?? {};
|
|
1278
|
+
const swapId = cowOrderId ? encodeSwapId({ type: "cowswap_rift", cowOrderId, riftOrderId: swap.id }) : encodeSwapId({ type: "rift", riftOrderId: swap.id, chained });
|
|
1279
|
+
return {
|
|
1280
|
+
swapId,
|
|
1281
|
+
status: this.mapStatus(swap.status, chained),
|
|
1282
|
+
rift: swap,
|
|
1283
|
+
preswap
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
async executeDeposit(swap, params, direction) {
|
|
1287
|
+
if (direction === "from_btc") {
|
|
1288
|
+
await this.sendBitcoinFn({
|
|
1289
|
+
recipient: swap.deposit_vault_address,
|
|
1290
|
+
amountSats: swap.quote.from.amount
|
|
1291
|
+
});
|
|
1292
|
+
} else {
|
|
1293
|
+
await this.transferErc20(getTokenAddress(params.from), swap.deposit_vault_address, swap.quote.from.amount);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
async transferErc20(tokenAddress, to, amount) {
|
|
1297
|
+
const address = await this.getAddress();
|
|
1298
|
+
const hash = await this.walletClient.writeContract({
|
|
1299
|
+
account: address,
|
|
1300
|
+
address: tokenAddress,
|
|
1301
|
+
abi: erc20Abi,
|
|
1302
|
+
functionName: "transfer",
|
|
1303
|
+
args: [to, BigInt(amount)],
|
|
1304
|
+
chain: this.walletClient.chain
|
|
1305
|
+
});
|
|
1306
|
+
return hash;
|
|
1307
|
+
}
|
|
1308
|
+
async getAddress() {
|
|
1309
|
+
const addresses = await this.walletClient.getAddresses();
|
|
1310
|
+
const address = addresses[0];
|
|
1311
|
+
if (!address) {
|
|
1312
|
+
throw new Error("No address available in wallet");
|
|
1313
|
+
}
|
|
1314
|
+
return address;
|
|
1315
|
+
}
|
|
1316
|
+
async getRefundAddress(params) {
|
|
1317
|
+
if (params.from.chain.kind === "BITCOIN") {
|
|
1318
|
+
throw new Error("refundAddress is required for BTC swaps (Bitcoin refund address)");
|
|
1319
|
+
}
|
|
1320
|
+
return this.getAddress();
|
|
1321
|
+
}
|
|
1322
|
+
mapStatus(status, isChained) {
|
|
1323
|
+
return match2(status).with("waiting_user_deposit_initiated", () => "pending_deposit").with("waiting_user_deposit_confirmed", () => "deposit_detected").with("waiting_mm_deposit_initiated", () => "processing").with("waiting_mm_deposit_confirmed", () => isChained ? "processing" : "funds_received").with("settling", () => isChained ? "swapping" : "settling").with("settled", () => "completed").with("failed", () => "failed").with("refunding_user", () => "refunding").exhaustive();
|
|
1324
|
+
}
|
|
1325
|
+
async getSwapStatus(swapId) {
|
|
1326
|
+
const decoded = decodeSwapId(swapId);
|
|
1327
|
+
if (decoded.type === "rift") {
|
|
1328
|
+
const rift2 = await this.riftClient.getOrder(decoded.riftOrderId);
|
|
1329
|
+
return {
|
|
1330
|
+
swapId,
|
|
1331
|
+
status: this.mapStatus(rift2.status, decoded.chained),
|
|
1332
|
+
rift: rift2
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1335
|
+
const [preswap, rift] = await Promise.all([
|
|
1336
|
+
this.getCowSwapOrder(decoded.cowOrderId),
|
|
1337
|
+
this.riftClient.getOrder(decoded.riftOrderId)
|
|
1338
|
+
]);
|
|
1339
|
+
const status = this.computeOverallStatus(preswap.status, rift.status);
|
|
1340
|
+
return {
|
|
1341
|
+
swapId,
|
|
1342
|
+
status,
|
|
1343
|
+
rift,
|
|
1344
|
+
preswap
|
|
1345
|
+
};
|
|
1346
|
+
}
|
|
1347
|
+
async getCowSwapOrder(orderId) {
|
|
1348
|
+
const chains = ["mainnet", "base"];
|
|
1349
|
+
const maxRetries = 3;
|
|
1350
|
+
const retryDelayMs = 2000;
|
|
1351
|
+
for (let attempt = 0;attempt < maxRetries; attempt++) {
|
|
1352
|
+
const results = await Promise.allSettled(chains.map((chain) => this.fetchCowSwapOrder(chain, orderId)));
|
|
1353
|
+
const success = results.find((result) => result.status === "fulfilled");
|
|
1354
|
+
if (success)
|
|
1355
|
+
return success.value;
|
|
1356
|
+
if (attempt === maxRetries - 1) {
|
|
1357
|
+
const reasons = results.map((result) => result.status === "rejected" ? result.reason?.message ?? String(result.reason) : "unknown failure").join("; ");
|
|
1358
|
+
throw new Error(`Failed to fetch CowSwap order ${orderId}: ${reasons}`);
|
|
1359
|
+
}
|
|
1360
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
|
|
1361
|
+
}
|
|
1362
|
+
throw new Error(`Failed to fetch CowSwap order ${orderId}`);
|
|
1363
|
+
}
|
|
1364
|
+
async fetchCowSwapOrder(chain, orderId) {
|
|
1365
|
+
const response = await fetch(`https://api.cow.fi/${chain}/api/v1/orders/${orderId}`);
|
|
1366
|
+
if (!response.ok) {
|
|
1367
|
+
throw new Error(`HTTP ${response.status} from CowSwap ${chain} for order ${orderId}`);
|
|
1368
|
+
}
|
|
1369
|
+
return response.json();
|
|
1370
|
+
}
|
|
1371
|
+
computeOverallStatus(cowStatus, riftStatus) {
|
|
1372
|
+
if (cowStatus === "cancelled" || cowStatus === "expired") {
|
|
1373
|
+
return "failed";
|
|
1374
|
+
}
|
|
1375
|
+
if (cowStatus === "presignaturePending" || cowStatus === "open") {
|
|
1376
|
+
return "pending_deposit";
|
|
1377
|
+
}
|
|
1378
|
+
return this.mapStatus(riftStatus);
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
export {
|
|
1382
|
+
getSupportedModes,
|
|
1383
|
+
encodeSwapId,
|
|
1384
|
+
detectRoute,
|
|
1385
|
+
decodeSwapId,
|
|
1386
|
+
RiftSdk,
|
|
1387
|
+
CBBTC_ETHEREUM,
|
|
1388
|
+
CBBTC_BASE,
|
|
1389
|
+
BTC
|
|
1390
|
+
};
|