@ocap/tx-protocols 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/execute.d.mts +53 -0
- package/esm/execute.mjs +225 -0
- package/esm/index.d.mts +95 -0
- package/esm/index.mjs +100 -0
- package/esm/pipes/ensure-cost.d.mts +18 -0
- package/esm/pipes/ensure-cost.mjs +136 -0
- package/esm/pipes/ensure-gas.d.mts +14 -0
- package/esm/pipes/ensure-gas.mjs +38 -0
- package/esm/protocols/account/delegate.d.mts +4 -0
- package/esm/protocols/account/delegate.mjs +195 -0
- package/esm/protocols/account/migrate.d.mts +4 -0
- package/esm/protocols/account/migrate.mjs +130 -0
- package/esm/protocols/account/revoke-delegate.d.mts +4 -0
- package/esm/protocols/account/revoke-delegate.mjs +102 -0
- package/esm/protocols/asset/acquire-v2.d.mts +9 -0
- package/esm/protocols/asset/acquire-v2.mjs +213 -0
- package/esm/protocols/asset/acquire-v3.d.mts +17 -0
- package/esm/protocols/asset/acquire-v3.mjs +265 -0
- package/esm/protocols/asset/calls/transfer-token.d.mts +4 -0
- package/esm/protocols/asset/calls/transfer-token.mjs +36 -0
- package/esm/protocols/asset/calls/transfer.d.mts +4 -0
- package/esm/protocols/asset/calls/transfer.mjs +32 -0
- package/esm/protocols/asset/consume.d.mts +4 -0
- package/esm/protocols/asset/consume.mjs +92 -0
- package/esm/protocols/asset/create.d.mts +4 -0
- package/esm/protocols/asset/create.mjs +136 -0
- package/esm/protocols/asset/mint.d.mts +4 -0
- package/esm/protocols/asset/mint.mjs +156 -0
- package/esm/protocols/asset/pipes/exec-mint-hook.d.mts +24 -0
- package/esm/protocols/asset/pipes/exec-mint-hook.mjs +54 -0
- package/esm/protocols/asset/pipes/extract-factory-tokens.d.mts +21 -0
- package/esm/protocols/asset/pipes/extract-factory-tokens.mjs +22 -0
- package/esm/protocols/asset/pipes/verify-itx-address.d.mts +32 -0
- package/esm/protocols/asset/pipes/verify-itx-address.mjs +56 -0
- package/esm/protocols/asset/pipes/verify-itx-assets.d.mts +18 -0
- package/esm/protocols/asset/pipes/verify-itx-assets.mjs +27 -0
- package/esm/protocols/asset/pipes/verify-itx-variables.d.mts +17 -0
- package/esm/protocols/asset/pipes/verify-itx-variables.mjs +20 -0
- package/esm/protocols/asset/pipes/verify-mint-limit.d.mts +15 -0
- package/esm/protocols/asset/pipes/verify-mint-limit.mjs +14 -0
- package/esm/protocols/asset/update.d.mts +4 -0
- package/esm/protocols/asset/update.mjs +111 -0
- package/esm/protocols/factory/create.d.mts +29 -0
- package/esm/protocols/factory/create.mjs +155 -0
- package/esm/protocols/governance/claim-stake.d.mts +27 -0
- package/esm/protocols/governance/claim-stake.mjs +220 -0
- package/esm/protocols/governance/return-stake.d.mts +27 -0
- package/esm/protocols/governance/return-stake.mjs +211 -0
- package/esm/protocols/governance/revoke-stake.d.mts +27 -0
- package/esm/protocols/governance/revoke-stake.mjs +178 -0
- package/esm/protocols/governance/slash-stake.d.mts +17 -0
- package/esm/protocols/governance/slash-stake.mjs +213 -0
- package/esm/protocols/governance/stake.d.mts +15 -0
- package/esm/protocols/governance/stake.mjs +270 -0
- package/esm/protocols/rollup/claim-reward.d.mts +11 -0
- package/esm/protocols/rollup/claim-reward.mjs +322 -0
- package/esm/protocols/rollup/close.d.mts +4 -0
- package/esm/protocols/rollup/close.mjs +105 -0
- package/esm/protocols/rollup/create-block.d.mts +11 -0
- package/esm/protocols/rollup/create-block.mjs +303 -0
- package/esm/protocols/rollup/create.d.mts +4 -0
- package/esm/protocols/rollup/create.mjs +164 -0
- package/esm/protocols/rollup/join.d.mts +4 -0
- package/esm/protocols/rollup/join.mjs +152 -0
- package/esm/protocols/rollup/leave.d.mts +4 -0
- package/esm/protocols/rollup/leave.mjs +137 -0
- package/esm/protocols/rollup/migrate.d.mts +4 -0
- package/esm/protocols/rollup/migrate.mjs +85 -0
- package/esm/protocols/rollup/pause.d.mts +4 -0
- package/esm/protocols/rollup/pause.mjs +76 -0
- package/esm/protocols/rollup/pipes/ensure-validator.d.mts +6 -0
- package/esm/protocols/rollup/pipes/ensure-validator.mjs +12 -0
- package/esm/protocols/rollup/pipes/verify-evidence.d.mts +15 -0
- package/esm/protocols/rollup/pipes/verify-evidence.mjs +29 -0
- package/esm/protocols/rollup/pipes/verify-signers.d.mts +15 -0
- package/esm/protocols/rollup/pipes/verify-signers.mjs +36 -0
- package/esm/protocols/rollup/pipes/verify-status.d.mts +13 -0
- package/esm/protocols/rollup/pipes/verify-status.mjs +26 -0
- package/esm/protocols/rollup/resume.d.mts +4 -0
- package/esm/protocols/rollup/resume.mjs +79 -0
- package/esm/protocols/rollup/update.d.mts +4 -0
- package/esm/protocols/rollup/update.mjs +111 -0
- package/esm/protocols/token/create.d.mts +4 -0
- package/esm/protocols/token/create.mjs +150 -0
- package/esm/protocols/token/deposit-v2.d.mts +11 -0
- package/esm/protocols/token/deposit-v2.mjs +216 -0
- package/esm/protocols/token/withdraw-v2.d.mts +9 -0
- package/esm/protocols/token/withdraw-v2.mjs +222 -0
- package/esm/protocols/token-factory/burn.d.mts +15 -0
- package/esm/protocols/token-factory/burn.mjs +233 -0
- package/esm/protocols/token-factory/create.d.mts +4 -0
- package/esm/protocols/token-factory/create.mjs +254 -0
- package/esm/protocols/token-factory/mint.d.mts +15 -0
- package/esm/protocols/token-factory/mint.mjs +234 -0
- package/esm/protocols/token-factory/pipes/calc-reserve.d.mts +21 -0
- package/esm/protocols/token-factory/pipes/calc-reserve.mjs +34 -0
- package/esm/protocols/token-factory/pipes/verify-icon.d.mts +14 -0
- package/esm/protocols/token-factory/pipes/verify-icon.mjs +18 -0
- package/esm/protocols/token-factory/pipes/verify-ownership.d.mts +12 -0
- package/esm/protocols/token-factory/pipes/verify-ownership.mjs +63 -0
- package/esm/protocols/token-factory/pipes/verify-url.d.mts +12 -0
- package/esm/protocols/token-factory/pipes/verify-url.mjs +26 -0
- package/esm/protocols/token-factory/update.d.mts +10 -0
- package/esm/protocols/token-factory/update.mjs +152 -0
- package/esm/protocols/trade/exchange-v2.d.mts +9 -0
- package/esm/protocols/trade/exchange-v2.mjs +239 -0
- package/esm/protocols/trade/transfer-v2.d.mts +9 -0
- package/esm/protocols/trade/transfer-v2.mjs +226 -0
- package/esm/protocols/trade/transfer-v3.d.mts +17 -0
- package/esm/protocols/trade/transfer-v3.mjs +270 -0
- package/esm/util.d.mts +141 -0
- package/esm/util.mjs +278 -0
- package/lib/_virtual/rolldown_runtime.cjs +29 -0
- package/lib/execute.cjs +231 -0
- package/lib/execute.d.cts +53 -0
- package/lib/index.cjs +105 -0
- package/lib/index.d.cts +95 -0
- package/lib/pipes/ensure-cost.cjs +141 -0
- package/lib/pipes/ensure-cost.d.cts +18 -0
- package/lib/pipes/ensure-gas.cjs +41 -0
- package/lib/pipes/ensure-gas.d.cts +14 -0
- package/lib/protocols/account/delegate.cjs +201 -0
- package/lib/protocols/account/delegate.d.cts +4 -0
- package/lib/protocols/account/migrate.cjs +135 -0
- package/lib/protocols/account/migrate.d.cts +4 -0
- package/lib/protocols/account/revoke-delegate.cjs +107 -0
- package/lib/protocols/account/revoke-delegate.d.cts +4 -0
- package/lib/protocols/asset/acquire-v2.cjs +216 -0
- package/lib/protocols/asset/acquire-v2.d.cts +9 -0
- package/lib/protocols/asset/acquire-v3.cjs +269 -0
- package/lib/protocols/asset/acquire-v3.d.cts +17 -0
- package/lib/protocols/asset/calls/transfer-token.cjs +40 -0
- package/lib/protocols/asset/calls/transfer-token.d.cts +4 -0
- package/lib/protocols/asset/calls/transfer.cjs +35 -0
- package/lib/protocols/asset/calls/transfer.d.cts +4 -0
- package/lib/protocols/asset/consume.cjs +95 -0
- package/lib/protocols/asset/consume.d.cts +4 -0
- package/lib/protocols/asset/create.cjs +140 -0
- package/lib/protocols/asset/create.d.cts +4 -0
- package/lib/protocols/asset/mint.cjs +159 -0
- package/lib/protocols/asset/mint.d.cts +4 -0
- package/lib/protocols/asset/pipes/exec-mint-hook.cjs +57 -0
- package/lib/protocols/asset/pipes/exec-mint-hook.d.cts +24 -0
- package/lib/protocols/asset/pipes/extract-factory-tokens.cjs +25 -0
- package/lib/protocols/asset/pipes/extract-factory-tokens.d.cts +21 -0
- package/lib/protocols/asset/pipes/verify-itx-address.cjs +59 -0
- package/lib/protocols/asset/pipes/verify-itx-address.d.cts +32 -0
- package/lib/protocols/asset/pipes/verify-itx-assets.cjs +29 -0
- package/lib/protocols/asset/pipes/verify-itx-assets.d.cts +18 -0
- package/lib/protocols/asset/pipes/verify-itx-variables.cjs +22 -0
- package/lib/protocols/asset/pipes/verify-itx-variables.d.cts +17 -0
- package/lib/protocols/asset/pipes/verify-mint-limit.cjs +16 -0
- package/lib/protocols/asset/pipes/verify-mint-limit.d.cts +15 -0
- package/lib/protocols/asset/update.cjs +114 -0
- package/lib/protocols/asset/update.d.cts +4 -0
- package/lib/protocols/factory/create.cjs +161 -0
- package/lib/protocols/factory/create.d.cts +29 -0
- package/lib/protocols/governance/claim-stake.cjs +223 -0
- package/lib/protocols/governance/claim-stake.d.cts +27 -0
- package/lib/protocols/governance/return-stake.cjs +215 -0
- package/lib/protocols/governance/return-stake.d.cts +27 -0
- package/lib/protocols/governance/revoke-stake.cjs +182 -0
- package/lib/protocols/governance/revoke-stake.d.cts +27 -0
- package/lib/protocols/governance/slash-stake.cjs +217 -0
- package/lib/protocols/governance/slash-stake.d.cts +17 -0
- package/lib/protocols/governance/stake.cjs +275 -0
- package/lib/protocols/governance/stake.d.cts +15 -0
- package/lib/protocols/rollup/claim-reward.cjs +328 -0
- package/lib/protocols/rollup/claim-reward.d.cts +11 -0
- package/lib/protocols/rollup/close.cjs +107 -0
- package/lib/protocols/rollup/close.d.cts +4 -0
- package/lib/protocols/rollup/create-block.cjs +309 -0
- package/lib/protocols/rollup/create-block.d.cts +11 -0
- package/lib/protocols/rollup/create.cjs +169 -0
- package/lib/protocols/rollup/create.d.cts +4 -0
- package/lib/protocols/rollup/join.cjs +157 -0
- package/lib/protocols/rollup/join.d.cts +4 -0
- package/lib/protocols/rollup/leave.cjs +141 -0
- package/lib/protocols/rollup/leave.d.cts +4 -0
- package/lib/protocols/rollup/migrate.cjs +87 -0
- package/lib/protocols/rollup/migrate.d.cts +4 -0
- package/lib/protocols/rollup/pause.cjs +78 -0
- package/lib/protocols/rollup/pause.d.cts +4 -0
- package/lib/protocols/rollup/pipes/ensure-validator.cjs +14 -0
- package/lib/protocols/rollup/pipes/ensure-validator.d.cts +6 -0
- package/lib/protocols/rollup/pipes/verify-evidence.cjs +32 -0
- package/lib/protocols/rollup/pipes/verify-evidence.d.cts +15 -0
- package/lib/protocols/rollup/pipes/verify-signers.cjs +39 -0
- package/lib/protocols/rollup/pipes/verify-signers.d.cts +15 -0
- package/lib/protocols/rollup/pipes/verify-status.cjs +28 -0
- package/lib/protocols/rollup/pipes/verify-status.d.cts +13 -0
- package/lib/protocols/rollup/resume.cjs +81 -0
- package/lib/protocols/rollup/resume.d.cts +4 -0
- package/lib/protocols/rollup/update.cjs +114 -0
- package/lib/protocols/rollup/update.d.cts +4 -0
- package/lib/protocols/token/create.cjs +156 -0
- package/lib/protocols/token/create.d.cts +4 -0
- package/lib/protocols/token/deposit-v2.cjs +219 -0
- package/lib/protocols/token/deposit-v2.d.cts +11 -0
- package/lib/protocols/token/withdraw-v2.cjs +225 -0
- package/lib/protocols/token/withdraw-v2.d.cts +9 -0
- package/lib/protocols/token-factory/burn.cjs +236 -0
- package/lib/protocols/token-factory/burn.d.cts +15 -0
- package/lib/protocols/token-factory/create.cjs +260 -0
- package/lib/protocols/token-factory/create.d.cts +4 -0
- package/lib/protocols/token-factory/mint.cjs +237 -0
- package/lib/protocols/token-factory/mint.d.cts +15 -0
- package/lib/protocols/token-factory/pipes/calc-reserve.cjs +38 -0
- package/lib/protocols/token-factory/pipes/calc-reserve.d.cts +21 -0
- package/lib/protocols/token-factory/pipes/verify-icon.cjs +22 -0
- package/lib/protocols/token-factory/pipes/verify-icon.d.cts +14 -0
- package/lib/protocols/token-factory/pipes/verify-ownership.cjs +66 -0
- package/lib/protocols/token-factory/pipes/verify-ownership.d.cts +12 -0
- package/lib/protocols/token-factory/pipes/verify-url.cjs +29 -0
- package/lib/protocols/token-factory/pipes/verify-url.d.cts +12 -0
- package/lib/protocols/token-factory/update.cjs +155 -0
- package/lib/protocols/token-factory/update.d.cts +10 -0
- package/lib/protocols/trade/exchange-v2.cjs +243 -0
- package/lib/protocols/trade/exchange-v2.d.cts +9 -0
- package/lib/protocols/trade/transfer-v2.cjs +229 -0
- package/lib/protocols/trade/transfer-v2.d.cts +9 -0
- package/lib/protocols/trade/transfer-v3.cjs +274 -0
- package/lib/protocols/trade/transfer-v3.d.cts +17 -0
- package/lib/util.cjs +296 -0
- package/lib/util.d.cts +141 -0
- package/package.json +49 -22
- package/tools/fixtures.ts +564 -0
- package/lib/execute.js +0 -254
- package/lib/index.js +0 -117
- package/lib/pipes/ensure-cost.js +0 -193
- package/lib/pipes/ensure-gas.js +0 -48
- package/lib/protocols/account/delegate.js +0 -223
- package/lib/protocols/account/migrate.js +0 -153
- package/lib/protocols/account/revoke-delegate.js +0 -110
- package/lib/protocols/asset/acquire-v2.js +0 -262
- package/lib/protocols/asset/acquire-v3.js +0 -330
- package/lib/protocols/asset/calls/README.md +0 -5
- package/lib/protocols/asset/calls/transfer-token.js +0 -36
- package/lib/protocols/asset/calls/transfer.js +0 -28
- package/lib/protocols/asset/consume.js +0 -105
- package/lib/protocols/asset/create.js +0 -151
- package/lib/protocols/asset/mint.js +0 -199
- package/lib/protocols/asset/pipes/exec-mint-hook.js +0 -62
- package/lib/protocols/asset/pipes/extract-factory-tokens.js +0 -18
- package/lib/protocols/asset/pipes/verify-itx-address.js +0 -54
- package/lib/protocols/asset/pipes/verify-itx-assets.js +0 -51
- package/lib/protocols/asset/pipes/verify-itx-variables.js +0 -26
- package/lib/protocols/asset/pipes/verify-mint-limit.js +0 -13
- package/lib/protocols/asset/update.js +0 -131
- package/lib/protocols/factory/create.js +0 -191
- package/lib/protocols/governance/claim-stake.js +0 -266
- package/lib/protocols/governance/return-stake.js +0 -248
- package/lib/protocols/governance/revoke-stake.js +0 -172
- package/lib/protocols/governance/slash-stake.js +0 -271
- package/lib/protocols/governance/stake.js +0 -303
- package/lib/protocols/rollup/claim-reward.js +0 -342
- package/lib/protocols/rollup/close.js +0 -104
- package/lib/protocols/rollup/create-block.js +0 -413
- package/lib/protocols/rollup/create.js +0 -197
- package/lib/protocols/rollup/join.js +0 -182
- package/lib/protocols/rollup/leave.js +0 -145
- package/lib/protocols/rollup/migrate.js +0 -85
- package/lib/protocols/rollup/pause.js +0 -75
- package/lib/protocols/rollup/pipes/ensure-validator.js +0 -12
- package/lib/protocols/rollup/pipes/verify-evidence.js +0 -37
- package/lib/protocols/rollup/pipes/verify-signers.js +0 -87
- package/lib/protocols/rollup/pipes/verify-status.js +0 -30
- package/lib/protocols/rollup/resume.js +0 -75
- package/lib/protocols/rollup/update.js +0 -122
- package/lib/protocols/token/create.js +0 -199
- package/lib/protocols/token/deposit-v2.js +0 -290
- package/lib/protocols/token/withdraw-v2.js +0 -305
- package/lib/protocols/token-factory/burn.js +0 -371
- package/lib/protocols/token-factory/create.js +0 -342
- package/lib/protocols/token-factory/mint.js +0 -385
- package/lib/protocols/token-factory/pipes/calc-reserve.js +0 -37
- package/lib/protocols/token-factory/pipes/verify-icon.js +0 -27
- package/lib/protocols/token-factory/pipes/verify-ownership.js +0 -93
- package/lib/protocols/token-factory/pipes/verify-url.js +0 -32
- package/lib/protocols/token-factory/update.js +0 -208
- package/lib/protocols/trade/exchange-v2.js +0 -239
- package/lib/protocols/trade/transfer-v2.js +0 -233
- package/lib/protocols/trade/transfer-v3.js +0 -333
- package/lib/util.js +0 -442
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { applyTokenChange, applyTokenUpdates } from "../../util.mjs";
|
|
2
|
+
import ensure_cost_default from "../../pipes/ensure-cost.mjs";
|
|
3
|
+
import ensure_gas_default from "../../pipes/ensure-gas.mjs";
|
|
4
|
+
import { Runner, pipes } from "@ocap/tx-pipeline";
|
|
5
|
+
import { CustomError } from "@ocap/util/lib/error";
|
|
6
|
+
import { account, asset } from "@ocap/state";
|
|
7
|
+
import { BN } from "@ocap/util";
|
|
8
|
+
import Debug from "debug";
|
|
9
|
+
import { getListField } from "@ocap/util/lib/get-list-field";
|
|
10
|
+
import { Joi, schemas } from "@arcblock/validator";
|
|
11
|
+
import { getRelatedAddresses } from "@ocap/util/lib/get-related-addr";
|
|
12
|
+
import { promisify } from "node:util";
|
|
13
|
+
import isEqual from "lodash/isEqual.js";
|
|
14
|
+
|
|
15
|
+
//#region src/protocols/trade/transfer-v3.ts
|
|
16
|
+
const debug = Debug("@ocap/tx-protocols:transfer-v2");
|
|
17
|
+
const runner = new Runner("transfer_v3");
|
|
18
|
+
const verifyAssetOwner = promisify(pipes.VerifyUpdater({
|
|
19
|
+
assetKey: "assets",
|
|
20
|
+
ownerKey: "owner"
|
|
21
|
+
}));
|
|
22
|
+
const schema = Joi.object({
|
|
23
|
+
inputsList: schemas.multiInput.min(1).required(),
|
|
24
|
+
outputsList: schemas.multiInput.min(1).required(),
|
|
25
|
+
data: Joi.any().optional().allow(null)
|
|
26
|
+
}).options({
|
|
27
|
+
stripUnknown: true,
|
|
28
|
+
noDefaults: false
|
|
29
|
+
});
|
|
30
|
+
runner.use(({ itx }, next) => {
|
|
31
|
+
const { error } = schema.validate(itx);
|
|
32
|
+
return next(error ? new CustomError("INVALID_TX", `Invalid itx: ${error.message}`) : void 0);
|
|
33
|
+
});
|
|
34
|
+
runner.use(pipes.VerifyTxInput({
|
|
35
|
+
fieldKey: "itx.inputs",
|
|
36
|
+
inputsKey: "inputs",
|
|
37
|
+
sendersKey: "senders",
|
|
38
|
+
tokensKey: "inputTokens",
|
|
39
|
+
assetsKey: "inputAssets"
|
|
40
|
+
}));
|
|
41
|
+
runner.use(pipes.VerifyTxInput({
|
|
42
|
+
fieldKey: "itx.outputs",
|
|
43
|
+
inputsKey: "outputs",
|
|
44
|
+
sendersKey: "receivers",
|
|
45
|
+
tokensKey: "outputTokens",
|
|
46
|
+
assetsKey: "outputAssets"
|
|
47
|
+
}));
|
|
48
|
+
runner.use(pipes.VerifyListSize({ listKey: [
|
|
49
|
+
"inputs",
|
|
50
|
+
"outputs",
|
|
51
|
+
"senders",
|
|
52
|
+
"receivers",
|
|
53
|
+
"inputTokens",
|
|
54
|
+
"inputAssets",
|
|
55
|
+
"outputTokens",
|
|
56
|
+
"outputAssets"
|
|
57
|
+
] }));
|
|
58
|
+
runner.use(pipes.VerifyInfo([
|
|
59
|
+
{
|
|
60
|
+
error: "INVALID_TX",
|
|
61
|
+
message: "An owner can not exist in tx input and output at the same time",
|
|
62
|
+
fn: (ctx) => {
|
|
63
|
+
const context = ctx;
|
|
64
|
+
return (context.senders || []).every((x) => (context.receivers || []).includes(x) === false);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
error: "INVALID_TX",
|
|
69
|
+
message: "Input and output token address does not match",
|
|
70
|
+
fn: (ctx) => {
|
|
71
|
+
const context = ctx;
|
|
72
|
+
return isEqual(context.inputTokens || [], context.outputTokens || []);
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
error: "INVALID_TX",
|
|
77
|
+
message: "Input and output asset address does not match",
|
|
78
|
+
fn: (ctx) => {
|
|
79
|
+
const context = ctx;
|
|
80
|
+
return isEqual(context.inputAssets || [], context.outputAssets || []);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
error: "INVALID_TX",
|
|
85
|
+
message: "Input and output token amount does not match",
|
|
86
|
+
fn: (ctx) => {
|
|
87
|
+
const context = ctx;
|
|
88
|
+
const inputs = context.inputs || [];
|
|
89
|
+
const outputs = context.outputs || [];
|
|
90
|
+
const inputTokens = context.inputTokens || [];
|
|
91
|
+
const inputMap = {};
|
|
92
|
+
const outputMap = {};
|
|
93
|
+
inputs.forEach((input) => {
|
|
94
|
+
getListField(input, "tokens").forEach(({ address, value }) => {
|
|
95
|
+
if (typeof inputMap[address] === "undefined") inputMap[address] = new BN(0);
|
|
96
|
+
inputMap[address] = inputMap[address].add(new BN(value));
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
outputs.forEach((output) => {
|
|
100
|
+
getListField(output, "tokens").forEach(({ address, value }) => {
|
|
101
|
+
if (typeof outputMap[address] === "undefined") outputMap[address] = new BN(0);
|
|
102
|
+
outputMap[address] = outputMap[address].add(new BN(value));
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
return inputTokens.every((address) => inputMap[address].eq(outputMap[address]));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
]));
|
|
109
|
+
runner.use(pipes.ExtractState({
|
|
110
|
+
from: "inputTokens",
|
|
111
|
+
to: "tokenStates",
|
|
112
|
+
status: "INVALID_TOKEN",
|
|
113
|
+
table: "token"
|
|
114
|
+
}));
|
|
115
|
+
runner.use((context, next) => {
|
|
116
|
+
const { tx: tx$1, inputTokens = [], tokenStates = [] } = context;
|
|
117
|
+
if (inputTokens.every((tokenAddress) => {
|
|
118
|
+
const tokenState = tokenStates.find((t) => t.address === tokenAddress);
|
|
119
|
+
return tokenState && tokenState.type === "CreditToken" && tokenState.issuer === tx$1.from;
|
|
120
|
+
})) return next();
|
|
121
|
+
return pipes.VerifyMultiSigV2({ signersKey: "senders" })(context, next);
|
|
122
|
+
});
|
|
123
|
+
runner.use(pipes.ExtractState({
|
|
124
|
+
from: "tx.from",
|
|
125
|
+
to: "senderState",
|
|
126
|
+
status: "OK",
|
|
127
|
+
table: "account"
|
|
128
|
+
}));
|
|
129
|
+
runner.use(pipes.ExtractState({
|
|
130
|
+
from: "senders",
|
|
131
|
+
to: "signerStates",
|
|
132
|
+
status: "INVALID_SIGNER_STATE",
|
|
133
|
+
table: "account"
|
|
134
|
+
}));
|
|
135
|
+
runner.use(pipes.ExtractState({
|
|
136
|
+
from: "receivers",
|
|
137
|
+
to: "receiverStates",
|
|
138
|
+
status: "OK",
|
|
139
|
+
table: "account"
|
|
140
|
+
}));
|
|
141
|
+
runner.use(pipes.VerifyBlocked({ stateKeys: ["signerStates", "receiverStates"] }));
|
|
142
|
+
runner.use(pipes.AntiLandAttack({
|
|
143
|
+
senderState: "signerStates",
|
|
144
|
+
receiverState: "receiverStates"
|
|
145
|
+
}));
|
|
146
|
+
runner.use(pipes.VerifyAccountMigration({
|
|
147
|
+
signerKey: "signerStates",
|
|
148
|
+
stateKey: "senderState",
|
|
149
|
+
addressKey: "tx.from"
|
|
150
|
+
}));
|
|
151
|
+
runner.use(pipes.VerifyTokenBalance({
|
|
152
|
+
ownerKey: "signerStates",
|
|
153
|
+
conditionKey: "inputs"
|
|
154
|
+
}));
|
|
155
|
+
runner.use(pipes.verifyTokenAccess({
|
|
156
|
+
statesKey: "tokenStates",
|
|
157
|
+
listFieldKey: "spenders",
|
|
158
|
+
accountKeys: ["senderState"],
|
|
159
|
+
errorMessage: "Account {address} is not allowed to transfer token {tokenAddress}"
|
|
160
|
+
}));
|
|
161
|
+
runner.use(pipes.ExtractState({
|
|
162
|
+
from: "inputAssets",
|
|
163
|
+
to: "assetStates",
|
|
164
|
+
status: "INVALID_ASSET",
|
|
165
|
+
table: "asset"
|
|
166
|
+
}));
|
|
167
|
+
runner.use(pipes.VerifyTransferrable({ assets: "assetStates" }));
|
|
168
|
+
runner.use(async (context, next) => {
|
|
169
|
+
const { inputs = [], assetStates = [], signerStates = [] } = context;
|
|
170
|
+
for (const input of inputs) {
|
|
171
|
+
const { owner } = input;
|
|
172
|
+
const assetsList = getListField(input, "assets");
|
|
173
|
+
const states = assetStates.filter((x) => assetsList.includes(x.address));
|
|
174
|
+
const signer = signerStates.find((x) => x.address === owner);
|
|
175
|
+
try {
|
|
176
|
+
await verifyAssetOwner({
|
|
177
|
+
assets: states,
|
|
178
|
+
owner: signer
|
|
179
|
+
});
|
|
180
|
+
} catch (err) {
|
|
181
|
+
return next(err);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return next();
|
|
185
|
+
});
|
|
186
|
+
runner.use(ensure_gas_default((context) => {
|
|
187
|
+
const ops = {
|
|
188
|
+
create: 0,
|
|
189
|
+
update: 0,
|
|
190
|
+
payment: 0
|
|
191
|
+
};
|
|
192
|
+
ops.update += (context.signerStates || []).length;
|
|
193
|
+
ops.update += (context.inputAssets || []).length;
|
|
194
|
+
(context.receivers || []).forEach((x) => {
|
|
195
|
+
if ((context.receiverStates || []).find((r) => r.address === x)) ops.update += 1;
|
|
196
|
+
else ops.create += 1;
|
|
197
|
+
});
|
|
198
|
+
if (context.senderState) ops.update += 1;
|
|
199
|
+
else ops.create += 1;
|
|
200
|
+
return ops;
|
|
201
|
+
}));
|
|
202
|
+
runner.use(ensure_cost_default({ attachSenderChanges: true }));
|
|
203
|
+
runner.use(pipes.TakeStateSnapshot());
|
|
204
|
+
runner.use(async (context, next) => {
|
|
205
|
+
const { tx: tx$1, inputs = [], outputs = [], receivers = [], senderState, signerStates = [], senderChange, updateVaults, receiverStates = [], assetStates = [], statedb } = context;
|
|
206
|
+
const signerUpdates = {};
|
|
207
|
+
inputs.forEach((x) => {
|
|
208
|
+
const { owner } = x;
|
|
209
|
+
signerUpdates[owner] = applyTokenUpdates(getListField(x, "tokens"), signerStates.find((s) => s.address === owner), "sub");
|
|
210
|
+
if (senderChange && owner === senderChange.address) signerUpdates[owner] = applyTokenChange(signerUpdates[owner], senderChange);
|
|
211
|
+
});
|
|
212
|
+
const receiverUpdates = {};
|
|
213
|
+
const assetUpdates = {};
|
|
214
|
+
outputs.forEach((x) => {
|
|
215
|
+
const { owner } = x;
|
|
216
|
+
const tokensList = getListField(x, "tokens");
|
|
217
|
+
const assetsList = getListField(x, "assets");
|
|
218
|
+
const ownerState = receiverStates.find((s) => getRelatedAddresses(s).includes(owner)) || { address: owner };
|
|
219
|
+
receiverUpdates[ownerState.address] = applyTokenUpdates(tokensList, ownerState, "add");
|
|
220
|
+
if (senderChange && ownerState.address === senderChange.address) receiverUpdates[ownerState.address] = applyTokenChange(receiverUpdates[ownerState.address], senderChange);
|
|
221
|
+
assetsList.forEach((address) => {
|
|
222
|
+
assetUpdates[address] = { owner: ownerState.address };
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
const sender = senderState ? senderState.address : tx$1.from;
|
|
226
|
+
const isAlsoSigner = !!signerUpdates[sender];
|
|
227
|
+
const isAlsoReceiver = !!receiverUpdates[sender];
|
|
228
|
+
const senderUpdatesLocal = {};
|
|
229
|
+
if (!isAlsoSigner && !isAlsoReceiver && senderState && senderChange) senderUpdatesLocal[sender] = applyTokenChange(senderState, senderChange);
|
|
230
|
+
debug("transfer-v3", {
|
|
231
|
+
signerUpdates,
|
|
232
|
+
receiverUpdates,
|
|
233
|
+
assetUpdates,
|
|
234
|
+
isAlsoSigner,
|
|
235
|
+
isAlsoReceiver
|
|
236
|
+
});
|
|
237
|
+
const [newSenderState, newSignerStates, newReceiverStates, newAssetStates] = await Promise.all([
|
|
238
|
+
statedb.account.updateOrCreate(senderState ?? null, account.updateOrCreate(senderState ?? null, Object.assign({
|
|
239
|
+
address: sender,
|
|
240
|
+
nonce: tx$1.nonce,
|
|
241
|
+
pk: tx$1.pk
|
|
242
|
+
}, signerUpdates[sender] || {}, receiverUpdates[sender] || {}, senderUpdatesLocal[sender] || {}), context), context),
|
|
243
|
+
Promise.all(signerStates.filter((x) => x.address !== sender).map((x) => statedb.account.update(x.address, account.update(x, signerUpdates[x.address], context), context))),
|
|
244
|
+
Promise.all(receivers.filter((x) => x !== sender).map((x) => {
|
|
245
|
+
const receiverState = receiverStates.find((s) => getRelatedAddresses(s).includes(x));
|
|
246
|
+
const owner = receiverState ? receiverState.address : x;
|
|
247
|
+
const updates = receiverUpdates[owner] || {};
|
|
248
|
+
return statedb.account.updateOrCreate(receiverState ?? null, account.updateOrCreate(receiverState ?? null, {
|
|
249
|
+
...updates,
|
|
250
|
+
address: owner
|
|
251
|
+
}, context), context);
|
|
252
|
+
})),
|
|
253
|
+
Promise.all(assetStates.map((x) => statedb.asset.update(x.address, asset.update(x, assetUpdates[x.address], context), context)))
|
|
254
|
+
]);
|
|
255
|
+
if (updateVaults) await updateVaults();
|
|
256
|
+
context.senderState = newSenderState;
|
|
257
|
+
context.signerStates = isAlsoSigner ? newSignerStates.concat(newSenderState) : newSignerStates;
|
|
258
|
+
context.receiverStates = isAlsoReceiver ? newReceiverStates.concat(newSenderState) : newReceiverStates;
|
|
259
|
+
context.assetStates = newAssetStates;
|
|
260
|
+
debug("transfer-v3", {
|
|
261
|
+
inputs,
|
|
262
|
+
outputs
|
|
263
|
+
});
|
|
264
|
+
next();
|
|
265
|
+
}, { persistError: true });
|
|
266
|
+
runner.use(pipes.VerifyStateDiff());
|
|
267
|
+
var transfer_v3_default = runner;
|
|
268
|
+
|
|
269
|
+
//#endregion
|
|
270
|
+
export { transfer_v3_default as default };
|
package/esm/util.d.mts
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { BN } from "@ocap/util";
|
|
2
|
+
import { AnyData } from "@ocap/message";
|
|
3
|
+
import { IChainConfig, IStakeState, TokenBalanceMap } from "@ocap/types";
|
|
4
|
+
|
|
5
|
+
//#region src/util.d.ts
|
|
6
|
+
/** Token update input for applyTokenUpdates */
|
|
7
|
+
interface ITokenUpdateInput {
|
|
8
|
+
address: string;
|
|
9
|
+
value: string;
|
|
10
|
+
}
|
|
11
|
+
/** State with token balances */
|
|
12
|
+
interface IStateWithTokens {
|
|
13
|
+
tokens?: TokenBalanceMap;
|
|
14
|
+
}
|
|
15
|
+
/** Token change input for applyTokenChange */
|
|
16
|
+
interface ITokenChange {
|
|
17
|
+
token: string;
|
|
18
|
+
delta: string | BN;
|
|
19
|
+
}
|
|
20
|
+
/** Tx fee calculation params */
|
|
21
|
+
interface ITxFeeParams {
|
|
22
|
+
amount: string;
|
|
23
|
+
feeRate: number;
|
|
24
|
+
maxFee: string;
|
|
25
|
+
minFee: string;
|
|
26
|
+
stringify?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/** Tx fee result (stringified) */
|
|
29
|
+
interface ITxFeeResultString {
|
|
30
|
+
total: string;
|
|
31
|
+
user: string;
|
|
32
|
+
reward: string;
|
|
33
|
+
}
|
|
34
|
+
/** Tx fee result (BN) */
|
|
35
|
+
interface ITxFeeResultBN {
|
|
36
|
+
total: BN;
|
|
37
|
+
user: BN;
|
|
38
|
+
reward: BN;
|
|
39
|
+
}
|
|
40
|
+
/** Context for getDelegationRequirements */
|
|
41
|
+
interface IDelegationContext {
|
|
42
|
+
txType: string;
|
|
43
|
+
config: IChainConfig;
|
|
44
|
+
}
|
|
45
|
+
/** Delegation requirement */
|
|
46
|
+
interface IDelegationRequirement {
|
|
47
|
+
type: string;
|
|
48
|
+
address: string;
|
|
49
|
+
value: string;
|
|
50
|
+
}
|
|
51
|
+
/** Split tx fee params */
|
|
52
|
+
interface ISplitTxFeeParams {
|
|
53
|
+
total: string;
|
|
54
|
+
shares?: Record<string, number>;
|
|
55
|
+
stringify?: boolean;
|
|
56
|
+
rateBase?: BN;
|
|
57
|
+
}
|
|
58
|
+
/** Rollup state for ensureBlockReward */
|
|
59
|
+
interface IRollupStateForReward {
|
|
60
|
+
address: string;
|
|
61
|
+
withdrawFeeRate: number;
|
|
62
|
+
minWithdrawFee: string;
|
|
63
|
+
maxWithdrawFee: string;
|
|
64
|
+
tokenAddress: string;
|
|
65
|
+
}
|
|
66
|
+
/** Tx state for ensureBlockReward */
|
|
67
|
+
interface ITxStateForReward {
|
|
68
|
+
type: string;
|
|
69
|
+
tx: {
|
|
70
|
+
from: string;
|
|
71
|
+
itxJson: {
|
|
72
|
+
maxFee?: string;
|
|
73
|
+
actualFee?: string;
|
|
74
|
+
token?: {
|
|
75
|
+
value: string;
|
|
76
|
+
};
|
|
77
|
+
proposer?: string;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/** Block reward result */
|
|
82
|
+
interface IBlockRewardResult {
|
|
83
|
+
mintedAmount: string;
|
|
84
|
+
burnedAmount: string;
|
|
85
|
+
rewardAmount: string;
|
|
86
|
+
stakeUpdates: Record<string, {
|
|
87
|
+
address: string;
|
|
88
|
+
token: string;
|
|
89
|
+
delta: string;
|
|
90
|
+
action: string;
|
|
91
|
+
}>;
|
|
92
|
+
accountUpdates: Record<string, {
|
|
93
|
+
address: string;
|
|
94
|
+
token: string;
|
|
95
|
+
delta: string;
|
|
96
|
+
action: string;
|
|
97
|
+
}>;
|
|
98
|
+
}
|
|
99
|
+
/** Tx input for isGasStakeInput */
|
|
100
|
+
interface ITxInput {
|
|
101
|
+
assetsList: string[];
|
|
102
|
+
tokensList: Array<{
|
|
103
|
+
address: string;
|
|
104
|
+
value: string;
|
|
105
|
+
}>;
|
|
106
|
+
}
|
|
107
|
+
/** Token input for fixTokenInput */
|
|
108
|
+
interface ITokenInputForFix {
|
|
109
|
+
tokensList: Array<{
|
|
110
|
+
address?: string;
|
|
111
|
+
value: string;
|
|
112
|
+
}>;
|
|
113
|
+
}
|
|
114
|
+
declare const RATE_BASE: any;
|
|
115
|
+
declare const decodeAnySafe: (encoded: AnyData | null) => AnyData | null;
|
|
116
|
+
declare const applyTokenUpdates: (tokens: ITokenUpdateInput[], state: IStateWithTokens | IStateWithTokens[] | null | undefined, operator: "add" | "sub") => IStateWithTokens | IStateWithTokens[] | Record<string, string>;
|
|
117
|
+
declare const applyTokenChange: (state: IStateWithTokens | IStateWithTokens[], change: ITokenChange) => IStateWithTokens | IStateWithTokens[] | Record<string, never>;
|
|
118
|
+
declare const fixTokenInput: (input: ITokenInputForFix, config: IChainConfig) => ITokenInputForFix;
|
|
119
|
+
declare const getTxFee: ({
|
|
120
|
+
amount,
|
|
121
|
+
feeRate,
|
|
122
|
+
maxFee,
|
|
123
|
+
minFee,
|
|
124
|
+
stringify
|
|
125
|
+
}: ITxFeeParams) => ITxFeeResultString | ITxFeeResultBN;
|
|
126
|
+
declare const getDelegationRequirements: (context: IDelegationContext) => IDelegationRequirement[];
|
|
127
|
+
declare const splitTxFee: ({
|
|
128
|
+
total,
|
|
129
|
+
shares,
|
|
130
|
+
stringify,
|
|
131
|
+
rateBase
|
|
132
|
+
}: ISplitTxFeeParams) => Record<string, string | BN>;
|
|
133
|
+
declare const getRewardLocker: (rollupAddress: string) => string;
|
|
134
|
+
declare const getBNSum: (...args: Array<string | string[]>) => string;
|
|
135
|
+
declare const isFixedFee: (x: ITxStateForReward) => boolean;
|
|
136
|
+
declare const ensureBlockReward: (rollupState: IRollupStateForReward, minReward: string, txStates: ITxStateForReward[]) => IBlockRewardResult;
|
|
137
|
+
declare const isGasStakeAddress: (sender: string, stakeId: string) => boolean;
|
|
138
|
+
declare const isGasStakeInput: (inputs: ITxInput[], token: string) => boolean;
|
|
139
|
+
declare const isGasStakeValid: (state: IStakeState | null | undefined, config: IChainConfig) => boolean;
|
|
140
|
+
//#endregion
|
|
141
|
+
export { RATE_BASE, applyTokenChange, applyTokenUpdates, decodeAnySafe, ensureBlockReward, fixTokenInput, getBNSum, getDelegationRequirements, getRewardLocker, getTxFee, isFixedFee, isGasStakeAddress, isGasStakeInput, isGasStakeValid, splitTxFee };
|
package/esm/util.mjs
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { CustomError } from "@ocap/util/lib/error";
|
|
2
|
+
import { toStakeAddress } from "@arcblock/did-util";
|
|
3
|
+
import { BN, fromTokenToUnit } from "@ocap/util";
|
|
4
|
+
import { decodeAny } from "@ocap/message";
|
|
5
|
+
import { getListField } from "@ocap/util/lib/get-list-field";
|
|
6
|
+
import cloneDeep from "lodash/cloneDeep.js";
|
|
7
|
+
import flattenDeep from "lodash/flattenDeep.js";
|
|
8
|
+
import groupBy from "lodash/groupBy.js";
|
|
9
|
+
|
|
10
|
+
//#region src/util.ts
|
|
11
|
+
const ZERO = new BN(0);
|
|
12
|
+
const RATE_BASE = new BN(1e4);
|
|
13
|
+
const decodeAnyNested = (encoded) => {
|
|
14
|
+
if (!encoded) return encoded;
|
|
15
|
+
if (Array.isArray(encoded)) return encoded.map((k) => decodeAnyNested(k));
|
|
16
|
+
if (typeof encoded === "object") {
|
|
17
|
+
const obj = encoded;
|
|
18
|
+
if (obj.typeUrl && obj.value) {
|
|
19
|
+
const result = decodeAny(obj);
|
|
20
|
+
result.value = decodeAnyNested(result.value);
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
return Object.keys(obj).reduce((acc, x) => {
|
|
24
|
+
acc[x] = decodeAnyNested(obj[x]);
|
|
25
|
+
return acc;
|
|
26
|
+
}, {});
|
|
27
|
+
}
|
|
28
|
+
return encoded;
|
|
29
|
+
};
|
|
30
|
+
const decodeAnySafe = (encoded) => {
|
|
31
|
+
if (!encoded) return null;
|
|
32
|
+
try {
|
|
33
|
+
const decoded = decodeAny(encoded);
|
|
34
|
+
if (decoded.value && typeof decoded.value === "object") decoded.value = decodeAnyNested(decoded.value);
|
|
35
|
+
return decoded;
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.error("decodeAnySafe failed", err);
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const applyTokenUpdates = (tokens, state, operator) => {
|
|
42
|
+
if (["add", "sub"].includes(operator) === false) throw new CustomError("UNEXPECTED_OPERATOR", `Invalid operator when applyTokenUpdates: ${operator}`);
|
|
43
|
+
if (!state) return {};
|
|
44
|
+
const states = cloneDeep((Array.isArray(state) ? state : [state]).map((x) => ({ tokens: x.tokens || {} })));
|
|
45
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
46
|
+
const { address, value } = tokens[i];
|
|
47
|
+
const expectedDelta = new BN(value);
|
|
48
|
+
let fulfilled = new BN(0);
|
|
49
|
+
for (let j = 0; j < states.length; j++) {
|
|
50
|
+
const origin = states[j].tokens;
|
|
51
|
+
const updated = cloneDeep(origin);
|
|
52
|
+
const balance = new BN(origin[address] || 0);
|
|
53
|
+
let newBalance;
|
|
54
|
+
if (operator === "add") {
|
|
55
|
+
fulfilled = fulfilled.add(expectedDelta);
|
|
56
|
+
newBalance = balance.add(expectedDelta);
|
|
57
|
+
} else {
|
|
58
|
+
const unfulfilledDelta = expectedDelta.sub(fulfilled);
|
|
59
|
+
const actualDelta = balance.lt(unfulfilledDelta) ? balance : unfulfilledDelta;
|
|
60
|
+
fulfilled = fulfilled.add(actualDelta);
|
|
61
|
+
newBalance = balance.sub(actualDelta);
|
|
62
|
+
}
|
|
63
|
+
updated[address] = newBalance.toString(10);
|
|
64
|
+
states[j].tokens = updated;
|
|
65
|
+
}
|
|
66
|
+
if (fulfilled.lt(expectedDelta)) throw new CustomError("INSUFFICIENT_FUND", `Negative token balance when applyTokenUpdates for ${address}, expected: ${expectedDelta.toString()} fulfilled: ${fulfilled.toString()}`);
|
|
67
|
+
}
|
|
68
|
+
return Array.isArray(state) ? states : states[0];
|
|
69
|
+
};
|
|
70
|
+
const applyTokenChange = (state, change) => {
|
|
71
|
+
const delta = typeof change.delta === "string" ? new BN(change.delta) : change.delta;
|
|
72
|
+
if (delta.gt(ZERO)) return applyTokenUpdates([{
|
|
73
|
+
address: change.token,
|
|
74
|
+
value: delta.toString(10)
|
|
75
|
+
}], state, "add");
|
|
76
|
+
return applyTokenUpdates([{
|
|
77
|
+
address: change.token,
|
|
78
|
+
value: delta.abs().toString(10)
|
|
79
|
+
}], state, "sub");
|
|
80
|
+
};
|
|
81
|
+
const fixTokenInput = (input, config) => {
|
|
82
|
+
getListField(input, "tokens").forEach((t) => {
|
|
83
|
+
if (!t.address) t.address = config.token.address;
|
|
84
|
+
});
|
|
85
|
+
return input;
|
|
86
|
+
};
|
|
87
|
+
const getTxFee = ({ amount, feeRate, maxFee, minFee, stringify = true }) => {
|
|
88
|
+
const userAmount = new BN(amount);
|
|
89
|
+
const maxFeeAmount = new BN(maxFee);
|
|
90
|
+
const minFeeAmount = new BN(minFee);
|
|
91
|
+
if (feeRate < 0) throw new CustomError("NEGATIVE_FEE_RATE", "Unexpected negative feeRate when getTxFee, abort!");
|
|
92
|
+
if (userAmount.isNeg()) throw new CustomError("NEGATIVE_AMOUNT", "Unexpected negative amount when getTxFee, abort!");
|
|
93
|
+
if (maxFeeAmount.isNeg()) throw new CustomError("NEGATIVE_MAX_FEE", "Unexpected negative maxFee when getTxFee, abort!");
|
|
94
|
+
if (minFeeAmount.isNeg()) throw new CustomError("NEGATIVE_MIN_FEE", "Unexpected negative minFee when getTxFee, abort!");
|
|
95
|
+
let rewardAmount = userAmount.mul(new BN(feeRate)).div(RATE_BASE);
|
|
96
|
+
if (rewardAmount.lt(minFeeAmount)) rewardAmount = minFeeAmount;
|
|
97
|
+
if (rewardAmount.gt(maxFeeAmount)) rewardAmount = maxFeeAmount;
|
|
98
|
+
const totalAmount = userAmount.add(rewardAmount);
|
|
99
|
+
if (stringify) return {
|
|
100
|
+
total: totalAmount.toString(10),
|
|
101
|
+
user: userAmount.toString(10),
|
|
102
|
+
reward: rewardAmount.toString(10)
|
|
103
|
+
};
|
|
104
|
+
return {
|
|
105
|
+
total: totalAmount,
|
|
106
|
+
user: userAmount,
|
|
107
|
+
reward: rewardAmount
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
const getDelegationRequirements = (context) => {
|
|
111
|
+
const { txType, config } = context;
|
|
112
|
+
const txFee = config.transaction.txFee[txType];
|
|
113
|
+
if (!txFee) return [];
|
|
114
|
+
return [{
|
|
115
|
+
type: "token",
|
|
116
|
+
address: config.token.address,
|
|
117
|
+
value: fromTokenToUnit(txFee, config.token.decimal).toString()
|
|
118
|
+
}];
|
|
119
|
+
};
|
|
120
|
+
const splitTxFee = ({ total, shares = {}, stringify = true, rateBase = RATE_BASE }) => {
|
|
121
|
+
const totalAmount = new BN(total);
|
|
122
|
+
if (totalAmount.isNeg()) throw new CustomError("NEGATIVE_TOTAL_AMOUNT", "Unexpected negative total when splitTxFee, abort!");
|
|
123
|
+
Object.keys(shares).forEach((key) => {
|
|
124
|
+
if (shares[key] < 0) throw new CustomError("NEGATIVE_FEE_SHARE", `Unexpected negative shares[${key}] when splitTxFee, abort!`);
|
|
125
|
+
});
|
|
126
|
+
if (Object.values(shares).reduce((sum, x) => sum.add(new BN(x)), new BN(0)).eq(rateBase) === false) throw new CustomError("INVALID_FEE_SHARE", "Invalid share config when splitTxFee, abort!");
|
|
127
|
+
const rewardShares = Object.keys(shares).reduce((acc, x) => {
|
|
128
|
+
acc[x] = totalAmount.mul(new BN(shares[x])).div(rateBase);
|
|
129
|
+
return acc;
|
|
130
|
+
}, {});
|
|
131
|
+
if (Object.values(rewardShares).reduce((sum, x) => sum.add(x), new BN(0)).eq(totalAmount) === false) {
|
|
132
|
+
const firstKey = Object.keys(rewardShares)[0];
|
|
133
|
+
const currentSum = Object.values(rewardShares).reduce((sum, x) => sum.add(x), new BN(0));
|
|
134
|
+
rewardShares[firstKey] = rewardShares[firstKey].add(totalAmount.sub(currentSum));
|
|
135
|
+
}
|
|
136
|
+
return Object.keys(rewardShares).reduce((acc, x) => {
|
|
137
|
+
acc[x] = stringify ? rewardShares[x].toString(10) : rewardShares[x];
|
|
138
|
+
return acc;
|
|
139
|
+
}, {});
|
|
140
|
+
};
|
|
141
|
+
const getRewardLocker = (rollupAddress) => toStakeAddress(rollupAddress, rollupAddress);
|
|
142
|
+
const getBNSum = (...args) => flattenDeep(args).reduce((sum, x) => sum.add(new BN(x)), new BN(0)).toString(10);
|
|
143
|
+
const isGovernanceTx = (x) => [
|
|
144
|
+
"pause_rollup",
|
|
145
|
+
"resume_rollup",
|
|
146
|
+
"migrate_rollup"
|
|
147
|
+
].includes(x.type);
|
|
148
|
+
const isFixedFee = (x) => !x.tx.itxJson.maxFee || new BN(x.tx.itxJson.maxFee).isZero();
|
|
149
|
+
const ensureBlockReward = (rollupState, minReward, txStates) => {
|
|
150
|
+
const { address, withdrawFeeRate, minWithdrawFee, maxWithdrawFee, tokenAddress } = rollupState;
|
|
151
|
+
const locker = getRewardLocker(address);
|
|
152
|
+
const result = {
|
|
153
|
+
mintedAmount: new BN(0),
|
|
154
|
+
burnedAmount: new BN(0),
|
|
155
|
+
rewardAmount: new BN(0),
|
|
156
|
+
stakeUpdates: {},
|
|
157
|
+
accountUpdates: {}
|
|
158
|
+
};
|
|
159
|
+
const maxPossibleReward = txStates.reduce((sum, x) => sum.add(new BN(isFixedFee(x) ? x.tx.itxJson.actualFee : x.tx.itxJson.maxFee)), new BN(0));
|
|
160
|
+
const minRequiredReward = new BN(minReward);
|
|
161
|
+
if (maxPossibleReward.lt(minRequiredReward)) throw new CustomError("INVALID_BLOCK", "Block reward does not match minReward requirement");
|
|
162
|
+
const dynamicFeeTxs = txStates.filter((x) => isGovernanceTx(x) === false && isFixedFee(x) === false);
|
|
163
|
+
const totalDynamicFee = dynamicFeeTxs.reduce((sum, x) => sum.add(new BN(x.tx.itxJson.maxFee)), new BN(0));
|
|
164
|
+
const totalFixedFee = txStates.filter((x) => isGovernanceTx(x) === false && isFixedFee(x)).reduce((sum, x) => sum.add(new BN(x.tx.itxJson.actualFee)), new BN(0));
|
|
165
|
+
const totalMissingFee = minRequiredReward.sub(totalFixedFee);
|
|
166
|
+
const minTxFee = minRequiredReward.div(new BN(txStates.length));
|
|
167
|
+
const changes = {
|
|
168
|
+
stake: [],
|
|
169
|
+
account: []
|
|
170
|
+
};
|
|
171
|
+
dynamicFeeTxs.forEach((x) => {
|
|
172
|
+
const maxFee = new BN(x.tx.itxJson.maxFee);
|
|
173
|
+
const defaults = getTxFee({
|
|
174
|
+
amount: x.tx.itxJson.token.value,
|
|
175
|
+
feeRate: withdrawFeeRate,
|
|
176
|
+
maxFee: maxWithdrawFee,
|
|
177
|
+
minFee: minWithdrawFee,
|
|
178
|
+
stringify: false
|
|
179
|
+
});
|
|
180
|
+
let actualFee = new BN(0);
|
|
181
|
+
if (totalMissingFee.isNeg()) actualFee = defaults.reward;
|
|
182
|
+
else actualFee = totalMissingFee.mul(maxFee).div(totalDynamicFee);
|
|
183
|
+
if (actualFee.isNeg()) throw new CustomError("NEGATIVE_ACTUAL_FEE", "Got negative actualFee for tx, abort!");
|
|
184
|
+
if (actualFee.lt(defaults.reward)) actualFee = defaults.reward;
|
|
185
|
+
if (actualFee.lt(minTxFee)) actualFee = minTxFee;
|
|
186
|
+
if (actualFee.lt(maxFee)) {
|
|
187
|
+
const refundFee = maxFee.sub(actualFee).toString(10);
|
|
188
|
+
changes.account.push({
|
|
189
|
+
address: x.tx.from,
|
|
190
|
+
delta: refundFee,
|
|
191
|
+
action: "refund"
|
|
192
|
+
});
|
|
193
|
+
changes.stake.push({
|
|
194
|
+
address: locker,
|
|
195
|
+
delta: `-${refundFee}`,
|
|
196
|
+
action: "refund"
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
x.tx.itxJson.actualFee = actualFee.toString(10);
|
|
200
|
+
});
|
|
201
|
+
txStates.filter((x) => isGovernanceTx(x) === false).forEach((x) => {
|
|
202
|
+
const user = x.tx.itxJson.token.value;
|
|
203
|
+
const fee = x.tx.itxJson.actualFee;
|
|
204
|
+
const total = getBNSum(user, fee);
|
|
205
|
+
if (x.type === "deposit_token_v2") {
|
|
206
|
+
result.rewardAmount = result.rewardAmount.add(new BN(fee));
|
|
207
|
+
result.mintedAmount = result.mintedAmount.add(new BN(total));
|
|
208
|
+
changes.stake.push({
|
|
209
|
+
address: toStakeAddress(x.tx.itxJson.proposer, address),
|
|
210
|
+
delta: total,
|
|
211
|
+
action: "mint"
|
|
212
|
+
});
|
|
213
|
+
} else if (x.type === "withdraw_token_v2") {
|
|
214
|
+
result.rewardAmount = result.rewardAmount.add(new BN(fee));
|
|
215
|
+
result.burnedAmount = result.burnedAmount.add(new BN(user));
|
|
216
|
+
changes.stake.push({
|
|
217
|
+
address: toStakeAddress(x.tx.from, address),
|
|
218
|
+
delta: `-${user}`,
|
|
219
|
+
action: "burn"
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
const grouped = {
|
|
224
|
+
stake: groupBy(changes.stake, "address"),
|
|
225
|
+
account: groupBy(changes.account, "address")
|
|
226
|
+
};
|
|
227
|
+
result.stakeUpdates = Object.keys(grouped.stake).reduce((acc, x) => {
|
|
228
|
+
acc[x] = {
|
|
229
|
+
address: x,
|
|
230
|
+
token: tokenAddress,
|
|
231
|
+
delta: getBNSum(...grouped.stake[x].map((c) => c.delta)),
|
|
232
|
+
action: grouped.stake[x][grouped.stake[x].length - 1].action
|
|
233
|
+
};
|
|
234
|
+
return acc;
|
|
235
|
+
}, {});
|
|
236
|
+
result.accountUpdates = Object.keys(grouped.account).reduce((acc, x) => {
|
|
237
|
+
acc[x] = {
|
|
238
|
+
address: x,
|
|
239
|
+
token: tokenAddress,
|
|
240
|
+
delta: getBNSum(...grouped.account[x].map((c) => c.delta)),
|
|
241
|
+
action: grouped.account[x][grouped.account[x].length - 1].action
|
|
242
|
+
};
|
|
243
|
+
return acc;
|
|
244
|
+
}, {});
|
|
245
|
+
return {
|
|
246
|
+
mintedAmount: result.mintedAmount.toString(10),
|
|
247
|
+
burnedAmount: result.burnedAmount.toString(10),
|
|
248
|
+
rewardAmount: result.rewardAmount.toString(10),
|
|
249
|
+
stakeUpdates: result.stakeUpdates,
|
|
250
|
+
accountUpdates: result.accountUpdates
|
|
251
|
+
};
|
|
252
|
+
};
|
|
253
|
+
const isGasStakeAddress = (sender, stakeId) => toStakeAddress(sender, sender) === stakeId;
|
|
254
|
+
const isGasStakeInput = (inputs, token) => {
|
|
255
|
+
if (inputs.length !== 1) return false;
|
|
256
|
+
const [{ assetsList, tokensList }] = inputs;
|
|
257
|
+
if (assetsList.length > 0) return false;
|
|
258
|
+
if (tokensList.length !== 1) return false;
|
|
259
|
+
const [tokenInput] = tokensList;
|
|
260
|
+
if (tokenInput.address !== token) return false;
|
|
261
|
+
return true;
|
|
262
|
+
};
|
|
263
|
+
const isGasStakeValid = (state, config) => {
|
|
264
|
+
if (!state) return false;
|
|
265
|
+
const { token, transaction } = config;
|
|
266
|
+
const { minStake } = transaction.txGas;
|
|
267
|
+
const expectedMin = fromTokenToUnit(minStake, token.decimal);
|
|
268
|
+
if (state.tokens) {
|
|
269
|
+
if (new BN(state.tokens[token.address] || 0).gte(expectedMin)) return true;
|
|
270
|
+
}
|
|
271
|
+
if (state.revokedTokens) {
|
|
272
|
+
if (new BN(state.revokedTokens[token.address] || 0).gte(expectedMin)) return true;
|
|
273
|
+
}
|
|
274
|
+
return false;
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
//#endregion
|
|
278
|
+
export { RATE_BASE, applyTokenChange, applyTokenUpdates, decodeAnySafe, ensureBlockReward, fixTokenInput, getBNSum, getDelegationRequirements, getRewardLocker, getTxFee, isFixedFee, isGasStakeAddress, isGasStakeInput, isGasStakeValid, splitTxFee };
|