@ocap/state 1.28.8 → 1.29.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.
Files changed (83) hide show
  1. package/esm/_virtual/rolldown_runtime.mjs +18 -0
  2. package/esm/contexts/state.d.mts +15 -0
  3. package/esm/contexts/state.mjs +17 -0
  4. package/esm/index.d.mts +20 -0
  5. package/esm/index.mjs +47 -0
  6. package/esm/states/account.d.mts +18 -0
  7. package/esm/states/account.mjs +91 -0
  8. package/esm/states/asset.d.mts +14 -0
  9. package/esm/states/asset.mjs +80 -0
  10. package/esm/states/blacklist.d.mts +36 -0
  11. package/esm/states/blacklist.mjs +71 -0
  12. package/esm/states/chain.d.mts +30 -0
  13. package/esm/states/chain.mjs +52 -0
  14. package/esm/states/delegation.d.mts +11 -0
  15. package/esm/states/delegation.mjs +42 -0
  16. package/esm/states/evidence.d.mts +12 -0
  17. package/esm/states/evidence.mjs +35 -0
  18. package/esm/states/factory.d.mts +12 -0
  19. package/esm/states/factory.mjs +76 -0
  20. package/esm/states/rollup-block.d.mts +13 -0
  21. package/esm/states/rollup-block.mjs +75 -0
  22. package/esm/states/rollup.d.mts +18 -0
  23. package/esm/states/rollup.mjs +215 -0
  24. package/esm/states/stake.d.mts +13 -0
  25. package/esm/states/stake.mjs +89 -0
  26. package/esm/states/token-factory.d.mts +13 -0
  27. package/esm/states/token-factory.mjs +76 -0
  28. package/esm/states/token.d.mts +14 -0
  29. package/esm/states/token.mjs +109 -0
  30. package/esm/states/tx.d.mts +233 -0
  31. package/esm/states/tx.mjs +867 -0
  32. package/esm/util.d.mts +6 -0
  33. package/esm/util.mjs +18 -0
  34. package/lib/_virtual/rolldown_runtime.cjs +43 -0
  35. package/lib/contexts/state.cjs +19 -0
  36. package/lib/contexts/state.d.cts +15 -0
  37. package/lib/index.cjs +121 -0
  38. package/lib/index.d.cts +20 -0
  39. package/lib/states/account.cjs +106 -0
  40. package/lib/states/account.d.cts +18 -0
  41. package/lib/states/asset.cjs +91 -0
  42. package/lib/states/asset.d.cts +14 -0
  43. package/lib/states/blacklist.cjs +74 -0
  44. package/lib/states/blacklist.d.cts +36 -0
  45. package/lib/states/chain.cjs +62 -0
  46. package/lib/states/chain.d.cts +30 -0
  47. package/lib/states/delegation.cjs +50 -0
  48. package/lib/states/delegation.d.cts +11 -0
  49. package/lib/states/evidence.cjs +44 -0
  50. package/lib/states/evidence.d.cts +12 -0
  51. package/lib/states/factory.cjs +85 -0
  52. package/lib/states/factory.d.cts +12 -0
  53. package/lib/states/rollup-block.cjs +85 -0
  54. package/lib/states/rollup-block.d.cts +13 -0
  55. package/lib/states/rollup.cjs +230 -0
  56. package/lib/states/rollup.d.cts +18 -0
  57. package/lib/states/stake.cjs +99 -0
  58. package/lib/states/stake.d.cts +13 -0
  59. package/lib/states/token-factory.cjs +86 -0
  60. package/lib/states/token-factory.d.cts +13 -0
  61. package/lib/states/token.cjs +121 -0
  62. package/lib/states/token.d.cts +14 -0
  63. package/lib/states/tx.cjs +889 -0
  64. package/lib/states/tx.d.cts +233 -0
  65. package/lib/util.cjs +19 -0
  66. package/lib/util.d.cts +6 -0
  67. package/package.json +46 -14
  68. package/lib/contexts/state.js +0 -19
  69. package/lib/index.js +0 -63
  70. package/lib/states/account.js +0 -95
  71. package/lib/states/asset.js +0 -91
  72. package/lib/states/blacklist.js +0 -103
  73. package/lib/states/chain.js +0 -49
  74. package/lib/states/delegation.js +0 -46
  75. package/lib/states/evidence.js +0 -35
  76. package/lib/states/factory.js +0 -92
  77. package/lib/states/rollup-block.js +0 -84
  78. package/lib/states/rollup.js +0 -297
  79. package/lib/states/stake.js +0 -83
  80. package/lib/states/token-factory.js +0 -74
  81. package/lib/states/token.js +0 -124
  82. package/lib/states/tx.js +0 -896
  83. package/lib/util.js +0 -28
@@ -0,0 +1,867 @@
1
+ import { __exportAll } from "../_virtual/rolldown_runtime.mjs";
2
+ import { toTypeInfo, types } from "@arcblock/did";
3
+ import { BN, fromTokenToUnit, isBN, toBase64 } from "@ocap/util";
4
+ import { CustomError } from "@ocap/util/lib/error";
5
+ import pick from "lodash/pick.js";
6
+ import get from "lodash/get.js";
7
+ import { decodeAny, formatMessage, fromTypeUrl } from "@ocap/message";
8
+ import { getListField } from "@ocap/util/lib/get-list-field";
9
+ import { getRelatedAddresses } from "@ocap/util/lib/get-related-addr";
10
+ import camelCase from "lodash/camelCase.js";
11
+ import groupBy from "lodash/groupBy.js";
12
+ import merge from "lodash/merge.js";
13
+ import upperFirst from "lodash/upperFirst.js";
14
+
15
+ //#region src/states/tx.ts
16
+ var tx_exports = /* @__PURE__ */ __exportAll({
17
+ FORGE_TOKEN_HOLDER: () => FORGE_TOKEN_HOLDER,
18
+ attachPaidTxGas: () => attachPaidTxGas,
19
+ create: () => create,
20
+ eachReceipts: () => eachReceipts,
21
+ getTxReceipts: () => getTxReceipts,
22
+ getTxReceiver: () => getTxReceiver,
23
+ getTxSender: () => getTxSender,
24
+ groupReceiptTokenChanges: () => groupReceiptTokenChanges,
25
+ mergeTxReceipts: () => mergeTxReceipts,
26
+ update: () => update,
27
+ verifyTxReceipts: () => verifyTxReceipts
28
+ });
29
+ const ZERO = new BN(0);
30
+ const { RoleType } = types;
31
+ const FORGE_TOKEN_HOLDER = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
32
+ const FORGE_FEE_RECEIVER = "z1EJUBdbPYqMiWHfRZvSAvbYeWcEHB19Xyfc";
33
+ const QLDB_MIGRATION_TIME = /* @__PURE__ */ new Date("2021-05-03T12:46:56.245Z");
34
+ function eachReceipts(receipts, callback) {
35
+ return (receipts || []).map((receipt) => {
36
+ return (receipt.changes || []).map((change) => callback(receipt.address, change));
37
+ }).flat();
38
+ }
39
+ function groupReceiptTokenChanges(receipts) {
40
+ const tokenChanges = {};
41
+ eachReceipts(receipts || [], (address, change) => {
42
+ if (toTypeInfo(change.target).role !== RoleType.ROLE_TOKEN) return;
43
+ if (change.value === "0") return;
44
+ if (!tokenChanges[address]) tokenChanges[address] = {};
45
+ const tokenChange = tokenChanges[address];
46
+ tokenChange[change.target] = new BN(tokenChange[change.target] || 0).add(new BN(change.value)).toString();
47
+ });
48
+ return tokenChanges;
49
+ }
50
+ const getTxReceiver = ({ tx, itx, typeUrl }) => {
51
+ switch (typeUrl) {
52
+ case "TransferTx":
53
+ case "ExchangeTx":
54
+ case "TransferV2Tx":
55
+ case "ExchangeV2Tx": return itx.to;
56
+ case "MintAssetTx": return itx.owner;
57
+ case "CreateTokenTx":
58
+ case "CreateAssetTx":
59
+ case "AcquireAssetV2Tx": return tx.delegator || tx.from;
60
+ case "AcquireAssetV3Tx": return itx.owner;
61
+ case "SetupSwapTx": return itx.receiver;
62
+ case "StakeTx": return itx.address;
63
+ default: return "";
64
+ }
65
+ };
66
+ const getTxSender = ({ tx, typeUrl }) => {
67
+ switch (typeUrl) {
68
+ case "TransferTx":
69
+ case "TransferV2Tx":
70
+ case "ExchangeTx":
71
+ case "ExchangeV2Tx":
72
+ case "AcquireAssetV2Tx":
73
+ case "SetupSwapTx":
74
+ case "WithdrawTokenTx":
75
+ case "StakeTx":
76
+ case "ReturnStakeTx":
77
+ case "SlashStakeTx": return tx.from;
78
+ default: return "";
79
+ }
80
+ };
81
+ const getReceiptChange = (receipts, { address, action }) => {
82
+ return receipts.filter((x) => !address || x.address === address).flatMap((x) => x.changes.filter((c) => !action || c.action === action));
83
+ };
84
+ const getTransferReceipts = (tx) => {
85
+ const senderReceipt = {
86
+ address: tx.from,
87
+ changes: []
88
+ };
89
+ const receiverReceipt = {
90
+ address: tx.itxJson.to,
91
+ changes: []
92
+ };
93
+ if (new BN(tx.itxJson.value).gt(ZERO)) {
94
+ senderReceipt.changes.push({
95
+ target: "",
96
+ action: "transfer",
97
+ value: `-${tx.itxJson.value}`
98
+ });
99
+ receiverReceipt.changes.push({
100
+ target: "",
101
+ action: "transfer",
102
+ value: tx.itxJson.value
103
+ });
104
+ }
105
+ if (Array.isArray(tx.itxJson.tokens)) tx.itxJson.tokens.forEach((x) => {
106
+ senderReceipt.changes.push({
107
+ target: x.address,
108
+ action: "transfer",
109
+ value: `-${x.value}`
110
+ });
111
+ receiverReceipt.changes.push({
112
+ target: x.address,
113
+ action: "transfer",
114
+ value: x.value
115
+ });
116
+ });
117
+ if (Array.isArray(tx.itxJson.assets)) tx.itxJson.assets.forEach((x) => {
118
+ senderReceipt.changes.push({
119
+ target: x,
120
+ action: "transfer",
121
+ value: "-1"
122
+ });
123
+ receiverReceipt.changes.push({
124
+ target: x,
125
+ action: "transfer",
126
+ value: "1"
127
+ });
128
+ });
129
+ return [senderReceipt, receiverReceipt];
130
+ };
131
+ const getReceiptsFromTxInput = (inputs, symbol, action = "transfer") => {
132
+ const receipts = [];
133
+ inputs.forEach((x) => {
134
+ const receipt = {
135
+ address: x.owner,
136
+ changes: []
137
+ };
138
+ getListField(x, "tokens").forEach(({ address, value }) => {
139
+ receipt.changes.push({
140
+ target: address,
141
+ action,
142
+ value: `${symbol}${value}`
143
+ });
144
+ });
145
+ getListField(x, "assets").forEach((address) => receipt.changes.push({
146
+ target: address,
147
+ action,
148
+ value: `${symbol}1`
149
+ }));
150
+ receipts.push(receipt);
151
+ });
152
+ return receipts;
153
+ };
154
+ const getReceiptsFromContext = (ctx) => {
155
+ if (Array.isArray(ctx.updatedAccounts)) {
156
+ const grouped = groupBy(ctx.updatedAccounts.filter((x) => x.address && x.delta), "address");
157
+ return Object.keys(grouped).map((k) => ({
158
+ address: k,
159
+ changes: grouped[k].map((x) => ({
160
+ target: x.token,
161
+ action: x.action || "transfer",
162
+ value: x.delta
163
+ }))
164
+ }));
165
+ }
166
+ return [];
167
+ };
168
+ /**
169
+ * Add receipts for tx that have gasPaid but no receipts
170
+ * This is used to handle legacy data that gas receipts were not properly recorded
171
+ */
172
+ const getReceiptsFromGasPaid = (tx, { config, typeUrl }) => {
173
+ const gasReceipt = getReceiptChange(tx.receipts || [], { action: "gas" });
174
+ const gasPaid = new BN(tx.gasPaid || 0);
175
+ if (gasReceipt.length) return [];
176
+ if (gasPaid.lte(ZERO)) return [];
177
+ const gasPayer = { AccountMigrateTx: tx.itxJson?.address }[typeUrl] || tx.from;
178
+ const gasReceiver = config?.vaults?.txGas?.[0] || FORGE_FEE_RECEIVER;
179
+ if (!gasPayer) return [];
180
+ return [{
181
+ address: gasPayer,
182
+ changes: [{
183
+ target: "",
184
+ action: "gas",
185
+ value: gasPaid.neg().toString()
186
+ }]
187
+ }, {
188
+ address: gasReceiver,
189
+ changes: [{
190
+ target: "",
191
+ action: "gas",
192
+ value: gasPaid.toString()
193
+ }]
194
+ }];
195
+ };
196
+ const getTransferV3Receipts = (tx) => [...getReceiptsFromTxInput(tx.itxJson.inputs || [], "-", "transfer"), ...getReceiptsFromTxInput(tx.itxJson.outputs || [], "", "transfer")];
197
+ const getExchangeReceipts = (tx, ctx) => {
198
+ const { senderState, receiverState } = ctx;
199
+ const { sender, receiver } = tx.itxJson;
200
+ const senderReceipt = {
201
+ address: senderState ? senderState.address : tx.from,
202
+ changes: []
203
+ };
204
+ const receiverReceipt = {
205
+ address: receiverState ? receiverState.address : tx.itxJson.to,
206
+ changes: []
207
+ };
208
+ if (sender && new BN(sender.value).gt(ZERO)) {
209
+ senderReceipt.changes.push({
210
+ target: "",
211
+ action: "transfer",
212
+ value: `-${sender.value}`
213
+ });
214
+ receiverReceipt.changes.push({
215
+ target: "",
216
+ action: "transfer",
217
+ value: sender.value
218
+ });
219
+ }
220
+ if (receiver && new BN(receiver.value).gt(ZERO)) {
221
+ receiverReceipt.changes.push({
222
+ target: "",
223
+ action: "transfer",
224
+ value: `-${receiver.value}`
225
+ });
226
+ senderReceipt.changes.push({
227
+ target: "",
228
+ action: "transfer",
229
+ value: receiver.value
230
+ });
231
+ }
232
+ if (Array.isArray(sender?.tokens)) sender.tokens.forEach((x) => {
233
+ senderReceipt.changes.push({
234
+ target: x.address,
235
+ action: "transfer",
236
+ value: `-${x.value}`
237
+ });
238
+ receiverReceipt.changes.push({
239
+ target: x.address,
240
+ action: "transfer",
241
+ value: x.value
242
+ });
243
+ });
244
+ if (Array.isArray(receiver?.tokens)) receiver.tokens.forEach((x) => {
245
+ receiverReceipt.changes.push({
246
+ target: x.address,
247
+ action: "transfer",
248
+ value: `-${x.value}`
249
+ });
250
+ senderReceipt.changes.push({
251
+ target: x.address,
252
+ action: "transfer",
253
+ value: x.value
254
+ });
255
+ });
256
+ if (Array.isArray(sender?.assets)) sender.assets.forEach((x) => {
257
+ senderReceipt.changes.push({
258
+ target: x,
259
+ action: "transfer",
260
+ value: "-1"
261
+ });
262
+ receiverReceipt.changes.push({
263
+ target: x,
264
+ action: "transfer",
265
+ value: "1"
266
+ });
267
+ });
268
+ if (Array.isArray(receiver?.assets)) receiver.assets.forEach((x) => {
269
+ receiverReceipt.changes.push({
270
+ target: x,
271
+ action: "transfer",
272
+ value: "-1"
273
+ });
274
+ senderReceipt.changes.push({
275
+ target: x,
276
+ action: "transfer",
277
+ value: "1"
278
+ });
279
+ });
280
+ return [senderReceipt, receiverReceipt];
281
+ };
282
+ const getCreateAssetReceipts = (tx) => {
283
+ return [{
284
+ address: tx.delegator || tx.from,
285
+ changes: [{
286
+ target: tx.itxJson.address,
287
+ action: "create",
288
+ value: "1"
289
+ }]
290
+ }];
291
+ };
292
+ const getMintAssetReceipts = (tx, ctx) => {
293
+ const { assets = [], address, owner } = tx.itxJson;
294
+ const ownerReceipt = {
295
+ address: ctx.ownerAddress || owner,
296
+ changes: [{
297
+ target: address,
298
+ action: "mint",
299
+ value: "1"
300
+ }]
301
+ };
302
+ assets.map((x) => ownerReceipt.changes.push({
303
+ target: x,
304
+ action: "consume",
305
+ value: "-1"
306
+ }));
307
+ return [ownerReceipt];
308
+ };
309
+ const getAcquireAssetReceipts = (tx, ctx) => {
310
+ const { assets = [], address } = tx.itxJson;
311
+ const ownerReceipt = {
312
+ address: tx.delegator || tx.from,
313
+ changes: [{
314
+ target: address,
315
+ action: "mint",
316
+ value: "1"
317
+ }]
318
+ };
319
+ const senderReceipt = {
320
+ address: tx.from,
321
+ changes: []
322
+ };
323
+ assets.map((x) => senderReceipt.changes.push({
324
+ target: x,
325
+ action: "consume",
326
+ value: "-1"
327
+ }));
328
+ if (ctx.factoryState) {
329
+ const { value, tokens } = ctx.factoryState.input;
330
+ if (new BN(value).gt(ZERO)) senderReceipt.changes.push({
331
+ target: "",
332
+ action: "consume",
333
+ value: `-${value}`
334
+ });
335
+ tokens.map((x) => senderReceipt.changes.push({
336
+ target: x.address,
337
+ action: "consume",
338
+ value: `-${x.value}`
339
+ }));
340
+ }
341
+ let mergedReceipts = [ownerReceipt, senderReceipt];
342
+ if (ownerReceipt.address === senderReceipt.address) {
343
+ senderReceipt.changes = [...ownerReceipt.changes, ...senderReceipt.changes];
344
+ mergedReceipts = [senderReceipt];
345
+ }
346
+ return [...mergedReceipts];
347
+ };
348
+ const getAcquireAssetV3Receipts = (tx) => {
349
+ const { inputs, owner, address } = tx.itxJson;
350
+ return [{
351
+ address: owner,
352
+ changes: [{
353
+ target: address,
354
+ action: "mint",
355
+ value: "1"
356
+ }]
357
+ }, ...getReceiptsFromTxInput(inputs, "-", "consume")];
358
+ };
359
+ const getStakeReceipts = (tx) => {
360
+ const { inputs, address } = tx.itxJson;
361
+ const tokens = {};
362
+ const assets = [];
363
+ inputs.forEach((input) => {
364
+ input.tokens.forEach((x) => {
365
+ if (typeof tokens[x.address] === "undefined") tokens[x.address] = new BN(0);
366
+ tokens[x.address] = tokens[x.address].add(new BN(x.value));
367
+ });
368
+ assets.push(...input.assets);
369
+ });
370
+ const stakeReceipt = {
371
+ address,
372
+ changes: []
373
+ };
374
+ assets.forEach((x) => stakeReceipt.changes.push({
375
+ target: x,
376
+ action: "stake",
377
+ value: "1"
378
+ }));
379
+ Object.keys(tokens).forEach((x) => stakeReceipt.changes.push({
380
+ target: x,
381
+ action: "stake",
382
+ value: tokens[x].toString(10)
383
+ }));
384
+ return [stakeReceipt, ...getReceiptsFromTxInput(inputs, "-", "stake")];
385
+ };
386
+ const getClaimStakeReceipts = (tx, ctx, action) => {
387
+ const { address } = tx.itxJson;
388
+ const { outputs } = ctx;
389
+ const tokens = {};
390
+ const assets = [];
391
+ outputs.forEach((output) => {
392
+ getListField(output, "tokens").forEach((x) => {
393
+ if (typeof tokens[x.address] === "undefined") tokens[x.address] = new BN(0);
394
+ tokens[x.address] = tokens[x.address].add(new BN(x.value));
395
+ });
396
+ assets.push(...getListField(output, "assets"));
397
+ });
398
+ const stakeReceipt = {
399
+ address,
400
+ changes: []
401
+ };
402
+ assets.forEach((x) => stakeReceipt.changes.push({
403
+ target: x,
404
+ action,
405
+ value: "-1"
406
+ }));
407
+ Object.keys(tokens).forEach((x) => stakeReceipt.changes.push({
408
+ target: x,
409
+ action,
410
+ value: `-${tokens[x].toString(10)}`
411
+ }));
412
+ return [stakeReceipt, ...getReceiptsFromTxInput(outputs, "", action)];
413
+ };
414
+ const getCreateTokenReceipts = (tx) => {
415
+ const { initialSupply, address } = tx.itxJson;
416
+ const balance = new BN(initialSupply).toString();
417
+ return [{
418
+ address: tx.delegator || tx.from,
419
+ changes: [{
420
+ target: address,
421
+ action: "mint",
422
+ value: balance
423
+ }]
424
+ }];
425
+ };
426
+ const getMintTokenReceipts = (tx, ctx) => {
427
+ const { inputChanges, tokenFactoryState } = ctx;
428
+ const { reserveAddress, tokenAddress, owner } = tokenFactoryState;
429
+ const hasFee = new BN(ctx.reserveFee || "0").gt(ZERO);
430
+ return inputChanges.map((input) => {
431
+ return {
432
+ address: input.address,
433
+ changes: [{
434
+ target: reserveAddress,
435
+ action: "swap",
436
+ value: input.delta
437
+ }]
438
+ };
439
+ }).concat([{
440
+ address: tx.itxJson.receiver,
441
+ changes: [{
442
+ target: tokenAddress,
443
+ action: "mint",
444
+ value: `${tx.itxJson.amount}`
445
+ }]
446
+ }]).concat(hasFee ? [{
447
+ address: owner,
448
+ changes: [{
449
+ target: reserveAddress,
450
+ action: "fee",
451
+ value: `${ctx.reserveFee}`
452
+ }]
453
+ }] : []);
454
+ };
455
+ const getBurnTokenReceipts = (tx, ctx) => {
456
+ const { inputChanges, tokenFactoryState } = ctx;
457
+ const { reserveAddress, tokenAddress, owner } = tokenFactoryState;
458
+ const fee = new BN(ctx.reserveFee || "0");
459
+ const receiveAmount = new BN(ctx.reserveAmount || "0").sub(fee);
460
+ return inputChanges.map((input) => {
461
+ return {
462
+ address: input.address,
463
+ changes: [{
464
+ target: tokenAddress,
465
+ action: "burn",
466
+ value: input.delta
467
+ }]
468
+ };
469
+ }).concat(receiveAmount.gt(ZERO) ? [{
470
+ address: tx.itxJson.receiver,
471
+ changes: [{
472
+ target: reserveAddress,
473
+ action: "swap",
474
+ value: receiveAmount.toString()
475
+ }]
476
+ }] : []).concat(fee.gt(ZERO) ? [{
477
+ address: owner,
478
+ changes: [{
479
+ target: reserveAddress,
480
+ action: "fee",
481
+ value: fee.toString()
482
+ }]
483
+ }] : []);
484
+ };
485
+ const getDepositTokenReceipts = (tx) => [{
486
+ address: tx.itxJson.address,
487
+ changes: [{
488
+ target: "",
489
+ action: "mint",
490
+ value: tx.itxJson.value
491
+ }]
492
+ }];
493
+ const getWithdrawFee = (value) => {
494
+ let fee = new BN(value).abs().mul(new BN("1")).div(new BN("1000"));
495
+ fee = BN.min(fee, new BN("100000000000000000000"));
496
+ fee = BN.max(fee, new BN("1000000000000000000"));
497
+ return fee;
498
+ };
499
+ const getWithdrawTokenReceipts = (tx) => {
500
+ const fee = getWithdrawFee(tx.itxJson.value);
501
+ return [{
502
+ address: tx.from,
503
+ changes: [{
504
+ target: "",
505
+ action: "lock",
506
+ value: `-${tx.itxJson.value}`
507
+ }, {
508
+ target: "",
509
+ action: "fee",
510
+ value: `-${fee.toString()}`
511
+ }]
512
+ }, {
513
+ address: FORGE_TOKEN_HOLDER,
514
+ changes: [{
515
+ target: "",
516
+ action: "lock",
517
+ value: tx.itxJson.value
518
+ }, {
519
+ target: "",
520
+ action: "fee",
521
+ value: fee.toString()
522
+ }]
523
+ }];
524
+ };
525
+ const getRevokeWithdrawReceipts = (tx, ctx) => {
526
+ const { withdrawTx } = ctx;
527
+ if (!withdrawTx?.tx?.from) return [];
528
+ if (withdrawTx.hash !== tx.itxJson.withdraw_tx_hash) return [];
529
+ const account = withdrawTx.tx.from;
530
+ const lockChange = (withdrawTx.receipts?.find((x) => x.address === account))?.changes?.find((x) => x.action === "lock");
531
+ if (!lockChange) return [];
532
+ const value = new BN(lockChange.value).abs();
533
+ const fee = getWithdrawFee(value.toString());
534
+ const revokeFee = fee.mul(new BN("5")).div(new BN("100"));
535
+ return [
536
+ {
537
+ address: account,
538
+ changes: [{
539
+ target: "",
540
+ action: "unlock",
541
+ value: value.toString()
542
+ }, {
543
+ target: "",
544
+ action: "fee",
545
+ value: fee.sub(revokeFee).toString()
546
+ }]
547
+ },
548
+ {
549
+ address: FORGE_TOKEN_HOLDER,
550
+ changes: [{
551
+ target: "",
552
+ action: "unlock",
553
+ value: `-${value.toString()}`
554
+ }, {
555
+ target: "",
556
+ action: "fee",
557
+ value: `-${fee.toString()}`
558
+ }]
559
+ },
560
+ {
561
+ address: FORGE_FEE_RECEIVER,
562
+ changes: [{
563
+ target: "",
564
+ action: "fee",
565
+ value: revokeFee.toString()
566
+ }]
567
+ }
568
+ ];
569
+ };
570
+ const getApproveWithdrawReceipts = (tx, ctx) => {
571
+ const { withdrawTx } = ctx;
572
+ if (!withdrawTx?.tx?.itxJson?.value) return [];
573
+ if (withdrawTx.hash !== tx.itxJson.withdraw_tx_hash) return [];
574
+ const { value } = withdrawTx.tx.itxJson;
575
+ const fee = getWithdrawFee(value);
576
+ return [{
577
+ address: FORGE_TOKEN_HOLDER,
578
+ changes: [{
579
+ target: "",
580
+ action: "burn",
581
+ value: `-${value}`
582
+ }, {
583
+ target: "",
584
+ action: "fee",
585
+ value: `-${fee.toString()}`
586
+ }]
587
+ }, {
588
+ address: FORGE_FEE_RECEIVER,
589
+ changes: [{
590
+ target: "",
591
+ action: "fee",
592
+ value: fee.toString()
593
+ }]
594
+ }];
595
+ };
596
+ const getSetupSwapReceipts = (tx) => [{
597
+ address: tx.from,
598
+ changes: [new BN(tx.itxJson.value).gt(ZERO) ? {
599
+ target: "",
600
+ action: "swap",
601
+ value: `-${tx.itxJson.value}`
602
+ } : null, ...tx.itxJson.assets.map((x) => ({
603
+ target: x,
604
+ action: "swap",
605
+ value: "-1"
606
+ }))].filter(Boolean)
607
+ }];
608
+ const getRetrieveSwapReceipts = (tx) => [{
609
+ address: tx.from,
610
+ changes: [new BN(tx.itxJson.value).gt(ZERO) ? {
611
+ target: "",
612
+ action: "swap",
613
+ value: tx.itxJson.value
614
+ } : null, ...tx.itxJson.assets.map((x) => ({
615
+ target: x,
616
+ action: "swap",
617
+ value: "1"
618
+ }))].filter(Boolean)
619
+ }];
620
+ const getMigrateReceipts = (tx, context) => {
621
+ const fromState = context.fromState || context.senderState;
622
+ if (!fromState?.tokens) return [];
623
+ if (!tx.itxJson?.address) return [];
624
+ const changes = Object.entries(fromState.tokens).filter(([, value]) => value && value !== "0").map(([address, value]) => ({
625
+ target: address,
626
+ action: "migrate",
627
+ value
628
+ }));
629
+ return changes.length ? [{
630
+ address: tx.itxJson.address,
631
+ changes
632
+ }] : [];
633
+ };
634
+ const getDeclareReceipts = (tx, ctx) => {
635
+ const txTime = new Date(ctx.time || "");
636
+ if (!tx.itxJson?.issuer) return [];
637
+ if (!ctx.config?.token) return [];
638
+ if (!ctx.time) return [];
639
+ if (txTime >= QLDB_MIGRATION_TIME) return [];
640
+ const gas = fromTokenToUnit("0.5", ctx.config.token.decimal || 18);
641
+ return [{
642
+ address: tx.itxJson.issuer,
643
+ changes: [{
644
+ target: ctx.config.token.address || "",
645
+ action: "gas",
646
+ value: `-${gas.toString()}`
647
+ }]
648
+ }, {
649
+ address: FORGE_FEE_RECEIVER,
650
+ changes: [{
651
+ target: ctx.config.token.address || "",
652
+ action: "gas",
653
+ value: gas.toString()
654
+ }]
655
+ }];
656
+ };
657
+ const mergeTxReceipts = (receipts) => {
658
+ const merged = {};
659
+ receipts.forEach((x) => {
660
+ if (merged[x.address] === void 0) merged[x.address] = x;
661
+ else merged[x.address].changes = [...merged[x.address].changes, ...x.changes];
662
+ });
663
+ return Object.values(merged);
664
+ };
665
+ /**
666
+ * Get list of accounts with migration history
667
+ */
668
+ const getAccountMigrationsFromContext = (ctx) => {
669
+ const migrationAddresses = [];
670
+ for (const [key, state] of Object.entries(ctx)) {
671
+ if (key.endsWith("State") && state && typeof state === "object" && "address" in state) migrationAddresses.push(getRelatedAddresses(state));
672
+ if (key.endsWith("States") && Array.isArray(state)) state.forEach((x) => {
673
+ if (x && typeof x === "object" && "address" in x) migrationAddresses.push(getRelatedAddresses(x));
674
+ });
675
+ }
676
+ return migrationAddresses;
677
+ };
678
+ /**
679
+ * Group receipts by target
680
+ */
681
+ const groupReceiptsByTarget = (receipts) => {
682
+ const targets = {};
683
+ for (const { address, changes } of receipts) for (const { target, value, action } of changes) {
684
+ if (!targets[target]) targets[target] = [];
685
+ targets[target].push({
686
+ address,
687
+ value: new BN(value),
688
+ action
689
+ });
690
+ }
691
+ return targets;
692
+ };
693
+ /**
694
+ * Verify transaction receipts
695
+ */
696
+ const verifyTxReceipts = (receipts, typeUrl, ctx = {}) => {
697
+ const targets = groupReceiptsByTarget(receipts);
698
+ const accountsWithMigration = getAccountMigrationsFromContext(ctx);
699
+ const skipActionsForType = {
700
+ MintAssetTx: ["mint", "consume"],
701
+ AcquireAssetV2Tx: ["mint", "consume"],
702
+ AcquireAssetV3Tx: ["mint", "consume"],
703
+ CreateAssetTx: ["create"],
704
+ CreateTokenTx: ["mint"],
705
+ CreateRollupBlockTx: ["burn", "mint"],
706
+ AccountMigrateTx: ["migrate"],
707
+ ApproveWithdrawTx: ["burn"],
708
+ SetupSwapTx: ["swap"],
709
+ RevokeSwapTx: ["swap"],
710
+ RetrieveSwapTx: ["swap"],
711
+ MintTokenTx: [
712
+ "swap",
713
+ "mint",
714
+ "fee"
715
+ ],
716
+ BurnTokenTx: [
717
+ "burn",
718
+ "swap",
719
+ "fee"
720
+ ]
721
+ }[typeUrl] || [];
722
+ for (const [target, changes] of Object.entries(targets)) {
723
+ const filteredChanges = changes.filter(({ action }) => {
724
+ if (!skipActionsForType.includes(action)) return true;
725
+ if (action === "consume" && toTypeInfo(target).role !== RoleType.ROLE_ASSET) return true;
726
+ return false;
727
+ });
728
+ const sum = filteredChanges.reduce((prev, current) => prev.add(current.value), new BN(0));
729
+ if (!sum.eq(ZERO)) throw new CustomError("INVALID_RECEIPTS_VALUE", `Receipts are not zero-sum, target: ${target}, sum: ${sum}`);
730
+ const accountValue = {};
731
+ for (const { address, value, action } of filteredChanges) {
732
+ const accounts = accountsWithMigration.find((x) => x.includes(address)) || [address];
733
+ for (const account of accounts) {
734
+ const key = `${action}-${account}`;
735
+ const existingValue = accountValue[key];
736
+ if (existingValue && (existingValue.gt(ZERO) && value.lt(ZERO) || existingValue.lt(ZERO) && value.gt(ZERO))) throw new CustomError("INVALID_RECEIPTS_ADDRESS", `Duplicate accounts in receipts, target: ${target}, address: ${address}, action: ${action}`);
737
+ accountValue[key] = value;
738
+ }
739
+ }
740
+ }
741
+ return true;
742
+ };
743
+ /**
744
+ * Create transaction receipts, each receipt has following properties:
745
+ *
746
+ * - address: the entity did that changed
747
+ * - changes:
748
+ * - target: the target address that was changed, can be token address, asset address
749
+ * - action: why the target has changed, such as consume, burn and transfer
750
+ * - value: the amount that has changed
751
+ */
752
+ const getTxReceipts = ({ tx, code }, ctx = {}) => {
753
+ const typeUrl = upperFirst(camelCase(tx?.itxJson?._type || ""));
754
+ if (code !== "OK") {
755
+ const receiptsFromContext = ctx.gasPaid ? getReceiptsFromContext(ctx) : [];
756
+ const receiptsFromGas = getReceiptsFromGasPaid(tx, {
757
+ ...ctx,
758
+ typeUrl
759
+ });
760
+ return receiptsFromContext.concat(receiptsFromGas);
761
+ }
762
+ let receipts = Array.isArray(ctx.receipts) ? ctx.receipts : [];
763
+ if (["TransferTx", "TransferV2Tx"].includes(typeUrl)) receipts = receipts.concat(getTransferReceipts(tx));
764
+ else if (["TransferV3Tx"].includes(typeUrl)) receipts = receipts.concat(getTransferV3Receipts(tx));
765
+ else if (["ExchangeTx", "ExchangeV2Tx"].includes(typeUrl)) receipts = receipts.concat(getExchangeReceipts(tx, ctx));
766
+ else if (["CreateAssetTx"].includes(typeUrl)) receipts = receipts.concat(getCreateAssetReceipts(tx));
767
+ else if (["MintAssetTx"].includes(typeUrl)) receipts = receipts.concat(getMintAssetReceipts(tx, ctx));
768
+ else if (["AcquireAssetV2Tx"].includes(typeUrl)) receipts = receipts.concat(getAcquireAssetReceipts(tx, ctx));
769
+ else if (["AcquireAssetV3Tx"].includes(typeUrl)) receipts = receipts.concat(getAcquireAssetV3Receipts(tx));
770
+ else if (["CreateTokenTx"].includes(typeUrl)) receipts = receipts.concat(getCreateTokenReceipts(tx));
771
+ else if (["MintTokenTx"].includes(typeUrl)) receipts = receipts.concat(getMintTokenReceipts(tx, ctx));
772
+ else if (["BurnTokenTx"].includes(typeUrl)) receipts = receipts.concat(getBurnTokenReceipts(tx, ctx));
773
+ else if (["DepositTokenTx"].includes(typeUrl)) receipts = receipts.concat(getDepositTokenReceipts(tx));
774
+ else if (["WithdrawTokenTx"].includes(typeUrl)) receipts = receipts.concat(getWithdrawTokenReceipts(tx));
775
+ else if (["SetupSwapTx"].includes(typeUrl)) receipts = receipts.concat(getSetupSwapReceipts(tx));
776
+ else if (["RetrieveSwapTx", "RevokeSwapTx"].includes(typeUrl)) receipts = receipts.concat(getRetrieveSwapReceipts(tx));
777
+ else if (["StakeTx"].includes(typeUrl)) receipts = receipts.concat(getStakeReceipts(tx));
778
+ else if (["ClaimStakeTx"].includes(typeUrl)) receipts = receipts.concat(getClaimStakeReceipts(tx, ctx, "claim"));
779
+ else if (["SlashStakeTx"].includes(typeUrl)) receipts = receipts.concat(getClaimStakeReceipts(tx, ctx, "slash"));
780
+ else if (["ReturnStakeTx"].includes(typeUrl)) receipts = receipts.concat(getClaimStakeReceipts(tx, ctx, "stake"));
781
+ else if (["RevokeWithdrawTx"].includes(typeUrl)) receipts = receipts.concat(getRevokeWithdrawReceipts(tx, ctx));
782
+ else if (["ApproveWithdrawTx"].includes(typeUrl)) receipts = receipts.concat(getApproveWithdrawReceipts(tx, ctx));
783
+ else if (["AccountMigrateTx"].includes(typeUrl)) receipts = receipts.concat(getMigrateReceipts(tx, ctx));
784
+ else if (["DeclareTx"].includes(typeUrl)) receipts = receipts.concat(getDeclareReceipts(tx, ctx));
785
+ receipts = receipts.concat(getReceiptsFromContext(ctx));
786
+ receipts = receipts.concat(getReceiptsFromGasPaid(tx, {
787
+ ...ctx,
788
+ typeUrl
789
+ }));
790
+ const merged = mergeTxReceipts(receipts).filter((x) => x.address && Array.isArray(x.changes) && x.changes.length);
791
+ const tokenAddr = get(ctx, "config.token.address", "");
792
+ if (tokenAddr) merged.forEach((x) => x.changes.forEach((c) => {
793
+ if (c.target === "") c.target = tokenAddr;
794
+ }));
795
+ return merged;
796
+ };
797
+ const create = (context, code = "OK", verifyReceipts = true) => {
798
+ const { txHash, txTime, tx, totalGas, itxExtras = {}, extra = {} } = context;
799
+ const itx = decodeAny(tx.itx).value;
800
+ const typeUrl = fromTypeUrl(tx.itx.typeUrl);
801
+ const typeName = tx.itx.typeUrl.split(":").pop() || "";
802
+ const itxJson = formatMessage(typeUrl, itx);
803
+ const result = {
804
+ code,
805
+ hash: txHash,
806
+ height: 0,
807
+ index: 0,
808
+ time: txTime,
809
+ sender: getTxSender({
810
+ tx,
811
+ itx,
812
+ typeUrl
813
+ }),
814
+ receiver: getTxReceiver({
815
+ tx,
816
+ itx,
817
+ typeUrl
818
+ }),
819
+ type: typeName,
820
+ tx: {
821
+ chainId: tx.chainId,
822
+ delegator: tx.delegator,
823
+ from: tx.from,
824
+ nonce: tx.nonce,
825
+ serviceFee: tx.serviceFee || "0",
826
+ gasFee: isBN(totalGas) ? totalGas.toString(10) : "0",
827
+ pk: tx.pk ? toBase64(tx.pk) : void 0,
828
+ signature: tx.signature ? toBase64(tx.signature) : void 0,
829
+ signatures: getListField(tx, "signatures").map((x) => formatMessage("Multisig", x)),
830
+ itx: { __typename: typeUrl },
831
+ itxJson: {
832
+ _type: typeUrl,
833
+ encoded_value: tx.itx.value,
834
+ type_url: tx.itx.typeUrl,
835
+ ...itxJson,
836
+ ...itxExtras,
837
+ data: itx.data || null
838
+ },
839
+ extra: extra.txExtra || null
840
+ }
841
+ };
842
+ result.receipts = getTxReceipts(result, context);
843
+ if (code === "OK" && verifyReceipts) try {
844
+ verifyTxReceipts(result.receipts, typeUrl, context);
845
+ result.receiptsVerified = true;
846
+ } catch (err) {
847
+ console.warn(JSON.stringify(result, null));
848
+ console.error("verifyTxReceipts error", err);
849
+ result.receiptsVerified = false;
850
+ }
851
+ attachPaidTxGas(result);
852
+ return result;
853
+ };
854
+ const attachPaidTxGas = (tx) => {
855
+ if (tx.tx.gasPaid) return tx;
856
+ let gasChange = null;
857
+ (tx.receipts || []).forEach((r) => {
858
+ const tmp = r.changes.find((c) => c.action === "gas" && c.value === tx.tx.gasFee);
859
+ if (tmp) gasChange = tmp;
860
+ });
861
+ tx.tx.gasPaid = gasChange ? tx.tx.gasFee : "0";
862
+ return tx;
863
+ };
864
+ const update = (state, updates) => merge(state, pick(updates, ["finalized", "tx.itxJson.actualFee"]));
865
+
866
+ //#endregion
867
+ export { FORGE_TOKEN_HOLDER, attachPaidTxGas, create, eachReceipts, getTxReceipts, getTxReceiver, getTxSender, groupReceiptTokenChanges, mergeTxReceipts, tx_exports, update, verifyTxReceipts };