@ickb/sdk 1000.0.40 → 1000.0.52
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/dist/cjs/index.d.ts +145 -34
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +220 -92
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.d.ts +145 -34
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +222 -94
- package/dist/esm/index.js.map +1 -1
- package/package.json +9 -9
- package/src/index.ts +339 -165
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ccc
|
|
1
|
+
import { ccc } from "@ckb-ccc/core";
|
|
2
2
|
import {
|
|
3
3
|
collect,
|
|
4
4
|
epochCompare,
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
epochAdd,
|
|
11
11
|
asyncBinarySearch,
|
|
12
12
|
RestrictedTransaction,
|
|
13
|
+
type ExchangeRatio,
|
|
14
|
+
binarySearch,
|
|
13
15
|
} from "@ickb/utils";
|
|
14
16
|
import {
|
|
15
17
|
convert,
|
|
@@ -20,197 +22,288 @@ import {
|
|
|
20
22
|
OwnedOwnerManager,
|
|
21
23
|
type IckbDepositCell,
|
|
22
24
|
type ReceiptCell,
|
|
23
|
-
type
|
|
25
|
+
type WithdrawalGroup,
|
|
24
26
|
} from "@ickb/core";
|
|
25
27
|
import { DaoManager, type DaoCell } from "@ickb/dao";
|
|
26
28
|
import {
|
|
27
29
|
Info,
|
|
28
30
|
OrderManager,
|
|
29
|
-
Ratio,
|
|
30
31
|
type OrderCell,
|
|
31
32
|
type OrderGroup,
|
|
32
|
-
type RatioLike,
|
|
33
33
|
} from "@ickb/order";
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Controller for managing iCKB operations.
|
|
37
|
+
*/
|
|
35
38
|
export class IckbController {
|
|
39
|
+
/**
|
|
40
|
+
* Creates an instance of IckbController.
|
|
41
|
+
* @param ickbUdtManager - The manager for iCKB UDT operations.
|
|
42
|
+
* @param logicManager - The logic manager for handling business logic.
|
|
43
|
+
* @param ownedOwnerManager - The manager for owned owner operations.
|
|
44
|
+
* @param orderManager - The manager for order operations.
|
|
45
|
+
* @param daoManager - The manager for DAO operations.
|
|
46
|
+
*/
|
|
36
47
|
constructor(
|
|
37
48
|
public readonly ickbUdtManager: IckbUdtManager,
|
|
38
49
|
public readonly logicManager: LogicManager,
|
|
39
50
|
public readonly ownedOwnerManager: OwnedOwnerManager,
|
|
40
51
|
public readonly orderManager: OrderManager,
|
|
41
52
|
public readonly daoManager: DaoManager,
|
|
53
|
+
public readonly bots: ccc.Script[],
|
|
42
54
|
) {}
|
|
43
55
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Creates an instance of IckbController from script dependencies.
|
|
58
|
+
* @param deps - The script dependencies.
|
|
59
|
+
* @param deps.udt - The script dependencies for UDT.
|
|
60
|
+
* @param deps.dao - The script dependencies for DAO.
|
|
61
|
+
* @param deps.ickbLogic - The script dependencies for iCKB logic.
|
|
62
|
+
* @param deps.ownedOwner - The script dependencies for owned owner.
|
|
63
|
+
* @param deps.order - The script dependencies for order.
|
|
64
|
+
* @returns An instance of IckbController.
|
|
65
|
+
*/
|
|
66
|
+
static fromDeps(
|
|
67
|
+
deps: {
|
|
68
|
+
udt: ScriptDeps;
|
|
69
|
+
dao: ScriptDeps;
|
|
70
|
+
ickbLogic: ScriptDeps;
|
|
71
|
+
ownedOwner: ScriptDeps;
|
|
72
|
+
order: ScriptDeps;
|
|
73
|
+
},
|
|
74
|
+
bots: ccc.Script[],
|
|
75
|
+
): IckbController {
|
|
59
76
|
const {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
77
|
+
udt: {
|
|
78
|
+
script: { codeHash, hashType },
|
|
79
|
+
cellDeps,
|
|
80
|
+
},
|
|
81
|
+
ickbLogic: { script: ickbLogicScript },
|
|
82
|
+
} = deps;
|
|
83
|
+
|
|
84
|
+
const daoManager = DaoManager.fromDeps(deps);
|
|
85
|
+
const udt = {
|
|
64
86
|
script: new ccc.Script(
|
|
65
87
|
codeHash,
|
|
66
88
|
hashType,
|
|
67
|
-
[
|
|
89
|
+
[ickbLogicScript.hash(), "00000080"].join("") as ccc.Hex,
|
|
68
90
|
),
|
|
69
91
|
cellDeps,
|
|
70
92
|
};
|
|
71
|
-
const ickbUdtManager = IckbUdtManager.fromDeps(
|
|
93
|
+
const ickbUdtManager = IckbUdtManager.fromDeps({ udt }, daoManager);
|
|
72
94
|
|
|
73
95
|
return new IckbController(
|
|
74
96
|
ickbUdtManager,
|
|
75
|
-
LogicManager.fromDeps(
|
|
76
|
-
OwnedOwnerManager.fromDeps(
|
|
77
|
-
OrderManager.fromDeps(
|
|
97
|
+
LogicManager.fromDeps(deps, daoManager, ickbUdtManager),
|
|
98
|
+
OwnedOwnerManager.fromDeps(deps, daoManager, ickbUdtManager),
|
|
99
|
+
OrderManager.fromDeps(deps, ickbUdtManager),
|
|
78
100
|
daoManager,
|
|
101
|
+
bots,
|
|
79
102
|
);
|
|
80
103
|
}
|
|
81
104
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Previews the conversion between CKB and UDT.
|
|
107
|
+
* @param conversion - The conversion parameters.
|
|
108
|
+
* @param conversion.isCkb2Udt - Indicates if the conversion is from CKB to UDT.
|
|
109
|
+
* @param conversion.amount - The amount to convert.
|
|
110
|
+
* @param conversion.rate - The exchange rate for conversion.
|
|
111
|
+
* @param conversion.fee - The fee for the conversion.
|
|
112
|
+
* @returns The rate and converted amount.
|
|
113
|
+
*
|
|
114
|
+
* Fee meaning examples:
|
|
115
|
+
* - 100000n => 0.001%
|
|
116
|
+
* - 10000n => 0.01%
|
|
117
|
+
* - 1000n => 0.1%
|
|
118
|
+
* - 100n => 1%
|
|
119
|
+
* - 10n => 10%
|
|
120
|
+
* - 1n => 100%
|
|
121
|
+
* - less than 1n => 0%
|
|
122
|
+
*/
|
|
123
|
+
static previewConversion(conversion: {
|
|
124
|
+
isCkb2Udt: boolean;
|
|
125
|
+
amount: ccc.FixedPoint;
|
|
126
|
+
rate: ExchangeRatio | ccc.ClientBlockHeader;
|
|
127
|
+
fee: ccc.Num;
|
|
128
|
+
}): { rate: ExchangeRatio; convertedAmount: ccc.FixedPoint } {
|
|
129
|
+
const { isCkb2Udt, amount, fee } = conversion;
|
|
130
|
+
let rate = conversion.rate;
|
|
131
|
+
rate = "dao" in rate ? ickbExchangeRatio(rate) : rate;
|
|
92
132
|
|
|
93
|
-
if (fee) {
|
|
94
|
-
const {
|
|
95
|
-
|
|
133
|
+
if (fee > ccc.Zero) {
|
|
134
|
+
const { ckbScale, udtScale } = rate;
|
|
135
|
+
rate = {
|
|
136
|
+
ckbScale,
|
|
137
|
+
// Worst case scenario where fee applies to the full amount
|
|
138
|
+
udtScale: udtScale + (isCkb2Udt ? 1n : -1n) * (udtScale / fee),
|
|
139
|
+
};
|
|
96
140
|
}
|
|
97
141
|
|
|
98
|
-
|
|
142
|
+
const convertedAmount =
|
|
143
|
+
amount > ccc.Zero ? convert(isCkb2Udt, amount, rate) : ccc.Zero;
|
|
144
|
+
|
|
145
|
+
return { rate, convertedAmount };
|
|
99
146
|
}
|
|
100
147
|
|
|
101
|
-
|
|
148
|
+
/**
|
|
149
|
+
* Converts between CKB and UDT based on the provided options.
|
|
150
|
+
* @param options - The conversion options.
|
|
151
|
+
* @param options.conversion - The conversion parameters.
|
|
152
|
+
* @param options.conversion.isCkb2Udt - Indicates if the conversion is from CKB to UDT.
|
|
153
|
+
* @param options.conversion.amount - The amount to convert.
|
|
154
|
+
* @param options.conversion.rate - The exchange rate for conversion.
|
|
155
|
+
* @param options.conversion.ckbMinMatchLog - Minimum CKB match log (optional).
|
|
156
|
+
* @param options.conversion.ckbMinFee - Minimum CKB fee (optional).
|
|
157
|
+
* @param options.signer - The signer for the transaction.
|
|
158
|
+
* @param options.userCells - The user's cells.
|
|
159
|
+
* @param options.system - The current system state.
|
|
160
|
+
* @param options.filters - The filters for the conversion.
|
|
161
|
+
* @param options.filters.isReadyOnly - Indicates if only ready conversions should be considered.
|
|
162
|
+
* @param options.filters.isFulfilledOnly - Indicates if only fulfilled conversions should be considered.
|
|
163
|
+
* @returns A promise that resolves to the conversion result or undefined.
|
|
164
|
+
*/
|
|
102
165
|
async convert(options: {
|
|
166
|
+
conversion: Parameters<typeof IckbController.previewConversion>[0] & {
|
|
167
|
+
ckbMinMatchLog?: number;
|
|
168
|
+
ckbMinFee?: ccc.Num;
|
|
169
|
+
};
|
|
103
170
|
signer: ccc.Signer;
|
|
104
|
-
isCkb2Udt: boolean;
|
|
105
|
-
amount: ccc.FixedPoint;
|
|
106
171
|
userCells: UserCells;
|
|
107
|
-
minCkbChange: ccc.FixedPoint;
|
|
108
172
|
system: SystemState;
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
denominator: ccc.Num;
|
|
173
|
+
filters: {
|
|
174
|
+
isReadyOnly: boolean;
|
|
175
|
+
isFulfilledOnly: boolean;
|
|
113
176
|
};
|
|
114
177
|
}): Promise<Conversion | undefined> {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
178
|
+
// Get user lock
|
|
179
|
+
const { system, signer } = options;
|
|
180
|
+
const lock = (await signer.getRecommendedAddressObj()).script;
|
|
181
|
+
|
|
182
|
+
// Normalize rate
|
|
183
|
+
const { rate } = IckbController.previewConversion({
|
|
184
|
+
...options.conversion,
|
|
185
|
+
amount: ccc.Zero,
|
|
186
|
+
});
|
|
187
|
+
const { isCkb2Udt, amount, ckbMinMatchLog } = options.conversion;
|
|
188
|
+
const info = Info.create(isCkb2Udt, rate, ckbMinMatchLog);
|
|
189
|
+
// feeRate is a good approximation of the bot tx size matching the order
|
|
190
|
+
const ckbMinFee = options.conversion.ckbMinFee ?? 10n * system.feeRate;
|
|
191
|
+
const conversion = {
|
|
192
|
+
isCkb2Udt,
|
|
193
|
+
amount,
|
|
194
|
+
rate,
|
|
195
|
+
info,
|
|
196
|
+
ckbMinFee,
|
|
197
|
+
lock,
|
|
198
|
+
};
|
|
199
|
+
|
|
120
200
|
const N = isCkb2Udt
|
|
121
201
|
? Number(amount / system.ckbDepositCap)
|
|
122
|
-
: system.
|
|
202
|
+
: system.depositPool.slice(0, 30).length;
|
|
203
|
+
|
|
123
204
|
const cache = Array<ReturnType<IckbController["convert"]>>(N);
|
|
124
|
-
const
|
|
205
|
+
const cachedAttempt = (
|
|
206
|
+
n: number,
|
|
207
|
+
): ReturnType<IckbController["convert"]> => {
|
|
125
208
|
n = N - n;
|
|
126
|
-
return (cache[n] =
|
|
127
|
-
cache[n] ?? this.attemptConversion(n, info, address, options));
|
|
209
|
+
return (cache[n] = cache[n] ?? this.attempt(n, conversion, options));
|
|
128
210
|
};
|
|
129
|
-
|
|
211
|
+
|
|
212
|
+
return cachedAttempt(
|
|
130
213
|
await asyncBinarySearch(
|
|
131
214
|
N,
|
|
132
|
-
async (n: number) => (await
|
|
215
|
+
async (n: number) => (await cachedAttempt(n)) !== undefined,
|
|
133
216
|
),
|
|
134
217
|
);
|
|
135
218
|
}
|
|
136
219
|
|
|
137
|
-
|
|
220
|
+
/**
|
|
221
|
+
* Attempts to perform a conversion based on the provided parameters.
|
|
222
|
+
* @param n - The size of deposit to make or withdraw from.
|
|
223
|
+
* @param conversion - The conversion parameters.
|
|
224
|
+
* @param conversion.isCkb2Udt - Indicates if the conversion is from CKB to UDT.
|
|
225
|
+
* @param conversion.amount - The amount to convert.
|
|
226
|
+
* @param conversion.rate - The exchange rate for conversion.
|
|
227
|
+
* @param conversion.info - Additional information for the conversion.
|
|
228
|
+
* @param conversion.ckbMinFee - Minimum CKB fee.
|
|
229
|
+
* @param conversion.lock - The script lock for the transaction.
|
|
230
|
+
* @param options - Additional options for the conversion.
|
|
231
|
+
* @returns A promise that resolves to the conversion result or undefined.
|
|
232
|
+
*/
|
|
233
|
+
private async attempt(
|
|
138
234
|
n: number,
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
235
|
+
conversion: {
|
|
236
|
+
isCkb2Udt: boolean;
|
|
237
|
+
amount: bigint;
|
|
238
|
+
rate: ExchangeRatio;
|
|
239
|
+
info: Info;
|
|
240
|
+
ckbMinFee: ccc.Num;
|
|
241
|
+
lock: ccc.Script;
|
|
242
|
+
},
|
|
243
|
+
options: Omit<Parameters<IckbController["convert"]>[0], "conversion">,
|
|
148
244
|
): ReturnType<IckbController["convert"]> {
|
|
149
245
|
try {
|
|
246
|
+
const { isCkb2Udt, rate, info, ckbMinFee, lock } = conversion;
|
|
247
|
+
const { signer, userCells, filters, system } = options;
|
|
248
|
+
const { feeRate, ckbDepositCap, exchangeRatio, depositPool } = system;
|
|
249
|
+
|
|
150
250
|
const tx = SmartTransaction.default();
|
|
151
|
-
const maturities: Epoch[] = [];
|
|
251
|
+
const maturities: ccc.Epoch[] = [[0n, 1n, 80n]]; // 3 minutes minimum
|
|
152
252
|
let ckbFee = ccc.Zero;
|
|
253
|
+
let amount = conversion.amount;
|
|
254
|
+
let udtWithdrawed = ccc.Zero;
|
|
153
255
|
|
|
256
|
+
// Core protocol
|
|
154
257
|
if (n > 0) {
|
|
155
258
|
if (isCkb2Udt) {
|
|
156
259
|
amount -= ckbDepositCap * BigInt(n);
|
|
157
|
-
if (amount <
|
|
260
|
+
if (amount < ccc.Zero) {
|
|
158
261
|
// Too many Deposits respectfully to the amount
|
|
159
262
|
return undefined;
|
|
160
263
|
}
|
|
161
|
-
this.logicManager.deposit(tx, n, ckbDepositCap,
|
|
264
|
+
this.logicManager.deposit(tx, n, ckbDepositCap, lock);
|
|
162
265
|
} else {
|
|
163
|
-
const myDeposits =
|
|
266
|
+
const myDeposits = depositPool.slice(0, n);
|
|
164
267
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
165
|
-
|
|
166
|
-
|
|
268
|
+
udtWithdrawed = myDeposits.slice(-1)[0]!.udtCumulative;
|
|
269
|
+
amount -= udtWithdrawed;
|
|
270
|
+
if (amount < ccc.Zero) {
|
|
167
271
|
// Too many Withdrawal Requests respectfully to the amount
|
|
168
272
|
return undefined;
|
|
169
273
|
}
|
|
170
|
-
this.ownedOwnerManager.requestWithdrawal(
|
|
171
|
-
tx,
|
|
172
|
-
myDeposits,
|
|
173
|
-
address.script,
|
|
174
|
-
);
|
|
274
|
+
this.ownedOwnerManager.requestWithdrawal(tx, myDeposits, lock);
|
|
175
275
|
maturities.push(...myDeposits.map((d) => d.maturity));
|
|
176
276
|
}
|
|
177
277
|
}
|
|
178
278
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
isCkb2Udt ? ccc.Zero : amount,
|
|
186
|
-
);
|
|
279
|
+
// Order protocol
|
|
280
|
+
if (amount > ccc.Zero) {
|
|
281
|
+
this.orderManager.mint(tx, lock, info, {
|
|
282
|
+
ckbValue: isCkb2Udt ? amount : ccc.Zero,
|
|
283
|
+
udtValue: isCkb2Udt ? ccc.Zero : amount,
|
|
284
|
+
});
|
|
187
285
|
|
|
188
|
-
const
|
|
189
|
-
const convertedAmount = convert(isCkb2Udt, amount, ratio);
|
|
286
|
+
const convertedAmount = convert(isCkb2Udt, amount, rate);
|
|
190
287
|
ckbFee = isCkb2Udt
|
|
191
288
|
? amount - convert(false, convertedAmount, exchangeRatio)
|
|
192
289
|
: convert(true, amount, exchangeRatio) - convertedAmount;
|
|
193
290
|
|
|
194
291
|
maturities.push(
|
|
195
|
-
|
|
196
|
-
orderMaturityEstimate(isCkb2Udt, amount, tip),
|
|
292
|
+
orderMaturityEstimate(isCkb2Udt, amount, udtWithdrawed, system),
|
|
197
293
|
);
|
|
198
|
-
}
|
|
199
294
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
295
|
+
// Check that order provides enough fee to the bot for being matched
|
|
296
|
+
if (ckbFee < ckbMinFee) {
|
|
297
|
+
return undefined;
|
|
298
|
+
}
|
|
204
299
|
}
|
|
205
300
|
|
|
206
301
|
const { receipts, withdrawalGroups, orders, udts, capacities } =
|
|
207
302
|
userCells;
|
|
208
303
|
|
|
209
304
|
this.logicManager.completeDeposit(tx, receipts);
|
|
210
|
-
this.ownedOwnerManager.withdraw(tx, withdrawalGroups,
|
|
211
|
-
|
|
212
|
-
});
|
|
213
|
-
this.orderManager.melt(tx, orders);
|
|
305
|
+
this.ownedOwnerManager.withdraw(tx, withdrawalGroups, filters);
|
|
306
|
+
this.orderManager.melt(tx, orders, filters);
|
|
214
307
|
this.ickbUdtManager.addUdts(tx, udts);
|
|
215
308
|
CapacityManager.addCapacities(tx, capacities);
|
|
216
309
|
|
|
@@ -234,9 +327,19 @@ export class IckbController {
|
|
|
234
327
|
}
|
|
235
328
|
}
|
|
236
329
|
|
|
237
|
-
|
|
330
|
+
/**
|
|
331
|
+
* Retrieves the L1 state based on the provided parameters.
|
|
332
|
+
* @param client - The client to interact with the blockchain.
|
|
333
|
+
* @param filter - The set of filters to apply.
|
|
334
|
+
* @param locks - The script locks to filter the user cells.
|
|
335
|
+
* @param options - Optional parameters for the state retrieval.
|
|
336
|
+
* @param options.minLockUp - The minimum lock-up period (optional).
|
|
337
|
+
* @param options.maxLockUp - The maximum lock-up period (optional).
|
|
338
|
+
* @returns A promise that resolves to an object containing the system state and user cells.
|
|
339
|
+
*/
|
|
238
340
|
async getL1State(
|
|
239
341
|
client: ccc.Client,
|
|
342
|
+
filter: Set<keyof UserCells | "depositPool" | "ckbBots">,
|
|
240
343
|
locks: ccc.Script[],
|
|
241
344
|
options?: {
|
|
242
345
|
minLockUp?: ccc.Epoch;
|
|
@@ -259,24 +362,43 @@ export class IckbController {
|
|
|
259
362
|
orders,
|
|
260
363
|
deposits,
|
|
261
364
|
withdrawalRequests,
|
|
365
|
+
botCapacities,
|
|
262
366
|
] = await Promise.all([
|
|
263
367
|
client.getFeeRate(),
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
368
|
+
filter.has("depositPool")
|
|
369
|
+
? collect(this.logicManager.findDeposits(client, opts))
|
|
370
|
+
: [],
|
|
371
|
+
filter.has("capacities")
|
|
372
|
+
? collect(CapacityManager.findCapacities(client, locks, opts))
|
|
373
|
+
: [],
|
|
374
|
+
filter.has("udts")
|
|
375
|
+
? collect(this.ickbUdtManager.findUdts(client, locks, opts))
|
|
376
|
+
: [],
|
|
377
|
+
filter.has("receipts")
|
|
378
|
+
? collect(this.logicManager.findReceipts(client, locks, opts))
|
|
379
|
+
: [],
|
|
380
|
+
filter.has("withdrawalGroups")
|
|
381
|
+
? collect(
|
|
382
|
+
this.ownedOwnerManager.findWithdrawalGroups(client, locks, opts),
|
|
383
|
+
)
|
|
384
|
+
: [],
|
|
385
|
+
filter.has("orders") ? collect(this.orderManager.findOrders(client)) : [],
|
|
386
|
+
filter.has("deposits")
|
|
387
|
+
? collect(this.daoManager.findDeposits(client, locks, opts))
|
|
388
|
+
: [],
|
|
389
|
+
filter.has("withdrawalRequests")
|
|
390
|
+
? collect(this.daoManager.findWithdrawalRequests(client, locks, opts))
|
|
391
|
+
: [],
|
|
392
|
+
filter.has("ckbBots")
|
|
393
|
+
? collect(CapacityManager.findCapacities(client, locks, opts))
|
|
394
|
+
: [],
|
|
273
395
|
]);
|
|
274
396
|
|
|
275
397
|
// Sort depositPool by maturity
|
|
276
398
|
depositPool.sort((a, b) => epochCompare(a.maturity, b.maturity));
|
|
277
399
|
// Calculate cumulative value of deposits in the pool
|
|
278
400
|
let udtCumulative = 0n;
|
|
279
|
-
const cumulativeDepositPool: SystemState["
|
|
401
|
+
const cumulativeDepositPool: SystemState["depositPool"] = [];
|
|
280
402
|
for (const deposit of depositPool) {
|
|
281
403
|
udtCumulative += deposit.udtValue;
|
|
282
404
|
cumulativeDepositPool.push({ ...deposit, udtCumulative });
|
|
@@ -295,6 +417,12 @@ export class IckbController {
|
|
|
295
417
|
const exchangeRatio = ickbExchangeRatio(tip);
|
|
296
418
|
const ckbDepositCap = convert(false, ICKB_DEPOSIT_CAP, exchangeRatio);
|
|
297
419
|
|
|
420
|
+
// Calculate the ckb ready to match incoming transactions
|
|
421
|
+
let ckbBots = botCapacities.reduce((acc, c) => acc + c.ckbValue, ccc.Zero);
|
|
422
|
+
// Each bot keeps a reserve of CKB for operations
|
|
423
|
+
ckbBots -= ccc.numFrom(this.bots.length) * ccc.fixedPointFrom("2000");
|
|
424
|
+
ckbBots = ckbBots > ccc.Zero ? ckbBots : ccc.Zero;
|
|
425
|
+
|
|
298
426
|
return {
|
|
299
427
|
system: {
|
|
300
428
|
feeRate,
|
|
@@ -302,8 +430,9 @@ export class IckbController {
|
|
|
302
430
|
exchangeRatio,
|
|
303
431
|
ckbDepositCap,
|
|
304
432
|
udtDepositCap: ICKB_DEPOSIT_CAP,
|
|
305
|
-
|
|
306
|
-
|
|
433
|
+
depositPool: cumulativeDepositPool,
|
|
434
|
+
orderPool: nonUserOrders,
|
|
435
|
+
ckbBots,
|
|
307
436
|
},
|
|
308
437
|
user: {
|
|
309
438
|
capacities,
|
|
@@ -316,84 +445,129 @@ export class IckbController {
|
|
|
316
445
|
},
|
|
317
446
|
};
|
|
318
447
|
}
|
|
319
|
-
|
|
320
|
-
// TODO: Allow filtering which cells are accounted for
|
|
321
|
-
static getBalance({
|
|
322
|
-
capacities,
|
|
323
|
-
udts,
|
|
324
|
-
receipts,
|
|
325
|
-
withdrawalGroups,
|
|
326
|
-
orders,
|
|
327
|
-
}: UserCells): Balance {
|
|
328
|
-
const cells: { cell: ccc.Cell; udtValue?: ccc.FixedPoint }[] = [
|
|
329
|
-
capacities,
|
|
330
|
-
udts,
|
|
331
|
-
receipts,
|
|
332
|
-
].flat();
|
|
333
|
-
for (const { owner, owned } of withdrawalGroups) {
|
|
334
|
-
cells.push(owned, owner);
|
|
335
|
-
}
|
|
336
|
-
for (const { master, order } of orders) {
|
|
337
|
-
cells.push(master, order);
|
|
338
|
-
}
|
|
339
|
-
let ckb = ccc.Zero;
|
|
340
|
-
let udt = ccc.Zero;
|
|
341
|
-
for (const cell of cells) {
|
|
342
|
-
ckb += cell.cell.cellOutput.capacity;
|
|
343
|
-
udt += cell.udtValue ?? ccc.Zero;
|
|
344
|
-
}
|
|
345
|
-
return {
|
|
346
|
-
ckb,
|
|
347
|
-
udt,
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
export interface Balance {
|
|
353
|
-
ckb: ccc.FixedPoint;
|
|
354
|
-
udt: ccc.FixedPoint;
|
|
355
448
|
}
|
|
356
449
|
|
|
450
|
+
/**
|
|
451
|
+
* Represents the state of the system.
|
|
452
|
+
*/
|
|
357
453
|
export interface SystemState {
|
|
454
|
+
/** The fee rate for transactions. */
|
|
358
455
|
feeRate: ccc.Num;
|
|
456
|
+
|
|
457
|
+
/** The tip for the current block header. */
|
|
359
458
|
tip: ccc.ClientBlockHeader;
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
459
|
+
|
|
460
|
+
/** The exchange ratio between CKB and UDT. */
|
|
461
|
+
exchangeRatio: ExchangeRatio;
|
|
462
|
+
|
|
463
|
+
/** The deposit cap expressed in CKB. */
|
|
364
464
|
ckbDepositCap: ccc.FixedPoint;
|
|
465
|
+
|
|
466
|
+
/** The deposit cap expressed in iCKB. */
|
|
365
467
|
udtDepositCap: ccc.FixedPoint;
|
|
366
|
-
|
|
367
|
-
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* The deposit pool containing iCKB deposit cells with cumulative iCKB amounts.
|
|
471
|
+
*/
|
|
472
|
+
depositPool: (IckbDepositCell & { udtCumulative: bigint })[];
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* The order pool containing non-user order cells.
|
|
476
|
+
*/
|
|
477
|
+
orderPool: OrderCell[];
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* The ckb held by bots ready to match incoming iCKB to CKB conversions.
|
|
481
|
+
*/
|
|
482
|
+
ckbBots: ccc.FixedPoint;
|
|
368
483
|
}
|
|
369
484
|
|
|
485
|
+
/**
|
|
486
|
+
* Represents the user cells in the system.
|
|
487
|
+
*/
|
|
370
488
|
export interface UserCells {
|
|
489
|
+
/** The capacities owned by the user. */
|
|
371
490
|
capacities: CapacityCell[];
|
|
491
|
+
|
|
492
|
+
/** The UDTs owned by the user. */
|
|
372
493
|
udts: UdtCell[];
|
|
494
|
+
|
|
495
|
+
/** The receipts associated with the user's transactions. */
|
|
373
496
|
receipts: ReceiptCell[];
|
|
374
|
-
|
|
497
|
+
|
|
498
|
+
/** The withdrawal groups for the user. */
|
|
499
|
+
withdrawalGroups: WithdrawalGroup[];
|
|
500
|
+
|
|
501
|
+
/** The orders placed by the user. */
|
|
375
502
|
orders: OrderGroup[];
|
|
503
|
+
|
|
504
|
+
/** The classical/non-iCKB deposits made by the user. */
|
|
376
505
|
deposits: DaoCell[];
|
|
506
|
+
|
|
507
|
+
/** The classical/non-iCKB withdrawal requests made by the user. */
|
|
377
508
|
withdrawalRequests: DaoCell[];
|
|
378
509
|
}
|
|
379
510
|
|
|
511
|
+
/**
|
|
512
|
+
* Represents a conversion transaction.
|
|
513
|
+
*/
|
|
380
514
|
export interface Conversion {
|
|
515
|
+
/** The smart transaction associated with the conversion. */
|
|
381
516
|
tx: SmartTransaction;
|
|
517
|
+
|
|
518
|
+
/** The fee in CKB for the order created by the conversion. */
|
|
382
519
|
ckbFee: ccc.FixedPoint;
|
|
383
|
-
|
|
520
|
+
|
|
521
|
+
/** The estimated maturity epoch for conversion. */
|
|
522
|
+
maturity: ccc.Epoch;
|
|
384
523
|
}
|
|
385
524
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
525
|
+
/**
|
|
526
|
+
* Estimates the maturity of an order based on the type of conversion and amount.
|
|
527
|
+
*
|
|
528
|
+
* @param isCkb2Udt - A boolean indicating if the order is from CKB to UDT.
|
|
529
|
+
* @param amount - The amount of CKB or UDT involved in the order.
|
|
530
|
+
* @param udtWithdrawed - The amount of UDT that has been withdrawn by the current conversion via core.
|
|
531
|
+
* @param system - The current system state, which includes the block header tip, order pool, deposit pool, CKB bots, and exchange ratio.
|
|
532
|
+
*
|
|
533
|
+
* @returns The estimated maturity epoch for the order.
|
|
534
|
+
*/
|
|
389
535
|
function orderMaturityEstimate(
|
|
390
536
|
isCkb2Udt: boolean,
|
|
391
537
|
amount: bigint,
|
|
392
|
-
|
|
538
|
+
udtWithdrawed: ccc.FixedPoint,
|
|
539
|
+
system: SystemState,
|
|
393
540
|
): ccc.Epoch {
|
|
394
|
-
|
|
541
|
+
const { tip, orderPool, depositPool, ckbBots, exchangeRatio } = system;
|
|
542
|
+
|
|
543
|
+
// Maturity estimate a priori, the estimation is based on:
|
|
544
|
+
// - CKB to iCKB orders at 100k CKB every minute
|
|
545
|
+
// - iCKB to CKB orders at 200 CKB every minute
|
|
546
|
+
let maturity = epochAdd(tip.epoch, [
|
|
395
547
|
0n,
|
|
396
548
|
1n + amount / ccc.fixedPointFrom(isCkb2Udt ? 100000 : 200),
|
|
397
549
|
4n * 60n,
|
|
398
550
|
]);
|
|
551
|
+
|
|
552
|
+
if (isCkb2Udt) {
|
|
553
|
+
return maturity;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
if (depositPool.length > 0) {
|
|
557
|
+
const udtWaiting = orderPool
|
|
558
|
+
.filter((o) => o.isUdt2CkbMatchable())
|
|
559
|
+
.reduce((acc, o) => acc + o.udtValue, ccc.Zero);
|
|
560
|
+
const udtAvailable = convert(isCkb2Udt, ckbBots, exchangeRatio);
|
|
561
|
+
|
|
562
|
+
const udtToConvert = amount + udtWithdrawed + udtWaiting - udtAvailable;
|
|
563
|
+
const i = binarySearch(
|
|
564
|
+
depositPool.length,
|
|
565
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
566
|
+
(n) => depositPool[n]!.udtCumulative >= udtToConvert,
|
|
567
|
+
);
|
|
568
|
+
|
|
569
|
+
maturity = depositPool[i]?.maturity ?? maturity;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
return maturity;
|
|
399
573
|
}
|