@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.
- package/esm/_virtual/rolldown_runtime.mjs +18 -0
- package/esm/contexts/state.d.mts +15 -0
- package/esm/contexts/state.mjs +17 -0
- package/esm/index.d.mts +20 -0
- package/esm/index.mjs +47 -0
- package/esm/states/account.d.mts +18 -0
- package/esm/states/account.mjs +91 -0
- package/esm/states/asset.d.mts +14 -0
- package/esm/states/asset.mjs +80 -0
- package/esm/states/blacklist.d.mts +36 -0
- package/esm/states/blacklist.mjs +71 -0
- package/esm/states/chain.d.mts +30 -0
- package/esm/states/chain.mjs +52 -0
- package/esm/states/delegation.d.mts +11 -0
- package/esm/states/delegation.mjs +42 -0
- package/esm/states/evidence.d.mts +12 -0
- package/esm/states/evidence.mjs +35 -0
- package/esm/states/factory.d.mts +12 -0
- package/esm/states/factory.mjs +76 -0
- package/esm/states/rollup-block.d.mts +13 -0
- package/esm/states/rollup-block.mjs +75 -0
- package/esm/states/rollup.d.mts +18 -0
- package/esm/states/rollup.mjs +215 -0
- package/esm/states/stake.d.mts +13 -0
- package/esm/states/stake.mjs +89 -0
- package/esm/states/token-factory.d.mts +13 -0
- package/esm/states/token-factory.mjs +76 -0
- package/esm/states/token.d.mts +14 -0
- package/esm/states/token.mjs +109 -0
- package/esm/states/tx.d.mts +233 -0
- package/esm/states/tx.mjs +867 -0
- package/esm/util.d.mts +6 -0
- package/esm/util.mjs +18 -0
- package/lib/_virtual/rolldown_runtime.cjs +43 -0
- package/lib/contexts/state.cjs +19 -0
- package/lib/contexts/state.d.cts +15 -0
- package/lib/index.cjs +121 -0
- package/lib/index.d.cts +20 -0
- package/lib/states/account.cjs +106 -0
- package/lib/states/account.d.cts +18 -0
- package/lib/states/asset.cjs +91 -0
- package/lib/states/asset.d.cts +14 -0
- package/lib/states/blacklist.cjs +74 -0
- package/lib/states/blacklist.d.cts +36 -0
- package/lib/states/chain.cjs +62 -0
- package/lib/states/chain.d.cts +30 -0
- package/lib/states/delegation.cjs +50 -0
- package/lib/states/delegation.d.cts +11 -0
- package/lib/states/evidence.cjs +44 -0
- package/lib/states/evidence.d.cts +12 -0
- package/lib/states/factory.cjs +85 -0
- package/lib/states/factory.d.cts +12 -0
- package/lib/states/rollup-block.cjs +85 -0
- package/lib/states/rollup-block.d.cts +13 -0
- package/lib/states/rollup.cjs +230 -0
- package/lib/states/rollup.d.cts +18 -0
- package/lib/states/stake.cjs +99 -0
- package/lib/states/stake.d.cts +13 -0
- package/lib/states/token-factory.cjs +86 -0
- package/lib/states/token-factory.d.cts +13 -0
- package/lib/states/token.cjs +121 -0
- package/lib/states/token.d.cts +14 -0
- package/lib/states/tx.cjs +889 -0
- package/lib/states/tx.d.cts +233 -0
- package/lib/util.cjs +19 -0
- package/lib/util.d.cts +6 -0
- package/package.json +46 -14
- package/lib/contexts/state.js +0 -19
- package/lib/index.js +0 -63
- package/lib/states/account.js +0 -95
- package/lib/states/asset.js +0 -91
- package/lib/states/blacklist.js +0 -103
- package/lib/states/chain.js +0 -49
- package/lib/states/delegation.js +0 -46
- package/lib/states/evidence.js +0 -35
- package/lib/states/factory.js +0 -92
- package/lib/states/rollup-block.js +0 -84
- package/lib/states/rollup.js +0 -297
- package/lib/states/stake.js +0 -83
- package/lib/states/token-factory.js +0 -74
- package/lib/states/token.js +0 -124
- package/lib/states/tx.js +0 -896
- 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 };
|