@ocap/tx-protocols 1.13.57 → 1.13.61
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/lib/protocols/account/declare.js +17 -17
- package/lib/protocols/account/delegate.js +23 -7
- package/lib/protocols/account/migrate.js +14 -8
- package/lib/protocols/account/revoke-delegate.js +14 -7
- package/lib/protocols/asset/create.js +21 -13
- package/lib/protocols/asset/update.js +15 -15
- package/lib/protocols/rollup/claim-reward.js +67 -58
- package/lib/protocols/rollup/create-block.js +60 -162
- package/lib/protocols/token/deposit-v2.js +43 -48
- package/lib/protocols/token/withdraw-v2.js +114 -23
- package/lib/util.js +160 -29
- package/package.json +12 -12
package/lib/util.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
const groupBy = require('lodash/groupBy');
|
|
2
|
+
const flattenDeep = require('lodash/flattenDeep');
|
|
3
|
+
const Error = require('@ocap/util/lib/error');
|
|
1
4
|
const { BN } = require('@ocap/util');
|
|
2
5
|
const { decodeAny } = require('@ocap/message');
|
|
3
6
|
const { toStakeAddress } = require('@arcblock/did-util');
|
|
@@ -50,7 +53,7 @@ const decodeAnySafe = (encoded) => {
|
|
|
50
53
|
|
|
51
54
|
const applyTokenUpdates = (tokens, state, operator) => {
|
|
52
55
|
if (['add', 'sub'].includes(operator) === false) {
|
|
53
|
-
throw new Error(`Invalid operator when applyTokenUpdates: ${operator}`);
|
|
56
|
+
throw new Error('FORBIDDEN', `Invalid operator when applyTokenUpdates: ${operator}`);
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
if (!state) {
|
|
@@ -65,7 +68,7 @@ const applyTokenUpdates = (tokens, state, operator) => {
|
|
|
65
68
|
const balance = new BN(oldTokens[address] || 0);
|
|
66
69
|
const newBalance = balance[operator](requirement);
|
|
67
70
|
if (newBalance.lt(ZERO)) {
|
|
68
|
-
throw new Error(`Negative token balance when applyTokenUpdates for ${address}`);
|
|
71
|
+
throw new Error('FORBIDDEN', `Negative token balance when applyTokenUpdates for ${address}`);
|
|
69
72
|
}
|
|
70
73
|
newTokens[address] = newBalance.toString(10);
|
|
71
74
|
}
|
|
@@ -75,10 +78,11 @@ const applyTokenUpdates = (tokens, state, operator) => {
|
|
|
75
78
|
};
|
|
76
79
|
};
|
|
77
80
|
const applyTokenChange = (state, change) => {
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
const delta = typeof change.delta === 'string' ? new BN(change.delta) : change.delta;
|
|
82
|
+
if (delta.gt(ZERO)) {
|
|
83
|
+
return applyTokenUpdates([{ address: change.token, value: delta.toString(10) }], state, 'add');
|
|
80
84
|
}
|
|
81
|
-
return applyTokenUpdates([{ address: change.token, value:
|
|
85
|
+
return applyTokenUpdates([{ address: change.token, value: delta.abs().toString(10) }], state, 'sub');
|
|
82
86
|
};
|
|
83
87
|
|
|
84
88
|
const fixTokenInput = (input, config) => {
|
|
@@ -92,53 +96,180 @@ const fixTokenInput = (input, config) => {
|
|
|
92
96
|
};
|
|
93
97
|
|
|
94
98
|
const RATE_BASE = new BN(10000);
|
|
95
|
-
const
|
|
99
|
+
const getTxFee = ({ amount, feeRate, maxFee, minFee, stringify = true }) => {
|
|
100
|
+
const userAmount = new BN(amount);
|
|
96
101
|
const maxFeeAmount = new BN(maxFee);
|
|
97
102
|
const minFeeAmount = new BN(minFee);
|
|
98
|
-
const totalAmount = new BN(total);
|
|
99
103
|
|
|
100
104
|
// total fee
|
|
101
|
-
let
|
|
102
|
-
if (
|
|
103
|
-
|
|
105
|
+
let rewardAmount = userAmount.mul(new BN(feeRate)).div(RATE_BASE);
|
|
106
|
+
if (rewardAmount.lt(minFeeAmount)) {
|
|
107
|
+
rewardAmount = minFeeAmount;
|
|
104
108
|
}
|
|
105
|
-
if (
|
|
106
|
-
|
|
109
|
+
if (rewardAmount.gt(maxFeeAmount)) {
|
|
110
|
+
rewardAmount = maxFeeAmount;
|
|
107
111
|
}
|
|
108
112
|
|
|
109
|
-
//
|
|
110
|
-
const
|
|
111
|
-
acc[x] = feeAmount.mul(new BN(shares[x])).div(RATE_BASE);
|
|
112
|
-
return acc;
|
|
113
|
-
}, {});
|
|
113
|
+
// total amount
|
|
114
|
+
const totalAmount = userAmount.add(rewardAmount);
|
|
114
115
|
|
|
115
116
|
if (stringify) {
|
|
116
117
|
return {
|
|
117
118
|
total: totalAmount.toString(10),
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
feeShares: Object.keys(feeShares).reduce((acc, x) => {
|
|
121
|
-
acc[x] = feeShares[x].toString(10);
|
|
122
|
-
return acc;
|
|
123
|
-
}, {}),
|
|
119
|
+
user: userAmount.toString(10),
|
|
120
|
+
reward: rewardAmount.toString(10),
|
|
124
121
|
};
|
|
125
122
|
}
|
|
126
123
|
|
|
127
124
|
return {
|
|
128
125
|
total: totalAmount,
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
feeShares,
|
|
126
|
+
user: userAmount,
|
|
127
|
+
reward: rewardAmount,
|
|
132
128
|
};
|
|
133
129
|
};
|
|
134
130
|
|
|
135
|
-
const
|
|
131
|
+
const splitTxFee = ({ total, shares = {}, stringify = true }) => {
|
|
132
|
+
const totalAmount = new BN(total);
|
|
133
|
+
const rewardShares = Object.keys(shares).reduce((acc, x) => {
|
|
134
|
+
acc[x] = totalAmount.mul(new BN(shares[x])).div(RATE_BASE);
|
|
135
|
+
return acc;
|
|
136
|
+
}, {});
|
|
137
|
+
|
|
138
|
+
return Object.keys(rewardShares).reduce((acc, x) => {
|
|
139
|
+
acc[x] = stringify ? rewardShares[x].toString(10) : rewardShares[x];
|
|
140
|
+
return acc;
|
|
141
|
+
}, {});
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const getRewardLocker = (rollupAddress) => toStakeAddress(rollupAddress, rollupAddress);
|
|
145
|
+
const getBNSum = (...args) => flattenDeep(args).reduce((sum, x) => sum.add(new BN(x)), new BN(0)).toString(10); // prettier-ignore
|
|
146
|
+
const isFixedFee = (x) => new BN(x.tx.itxJson.maxFee).isZero();
|
|
147
|
+
|
|
148
|
+
const ensureBlockReward = (rollupAddress, tokenAddress, minReward, txStates) => {
|
|
149
|
+
const locker = toStakeAddress(rollupAddress, rollupAddress);
|
|
150
|
+
|
|
151
|
+
const result = {
|
|
152
|
+
mintedAmount: new BN(0),
|
|
153
|
+
burnedAmount: new BN(0),
|
|
154
|
+
rewardAmount: new BN(0),
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
// 0. ensure reward requirement
|
|
158
|
+
const maxPossibleReward = txStates.reduce(
|
|
159
|
+
(sum, x) => sum.add(new BN(isFixedFee(x) ? x.tx.itxJson.actualFee : x.tx.itxJson.maxFee)),
|
|
160
|
+
new BN(0)
|
|
161
|
+
);
|
|
162
|
+
const minRequiredReward = new BN(minReward);
|
|
163
|
+
if (maxPossibleReward.lt(minRequiredReward)) {
|
|
164
|
+
throw new Error('INVALID_BLOCK', 'Block reward does not match minReward requirement');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// 1. find dynamic reward tx
|
|
168
|
+
const dynamicFeeTxs = txStates.filter((x) => isFixedFee(x) === false);
|
|
169
|
+
const totalDynamicFee = dynamicFeeTxs.reduce((sum, x) => sum.add(new BN(x.tx.itxJson.maxFee)), new BN(0));
|
|
170
|
+
|
|
171
|
+
const fixedFeeTxs = txStates.filter((x) => isFixedFee(x));
|
|
172
|
+
const totalFixedFee = fixedFeeTxs.reduce((sum, x) => sum.add(new BN(x.tx.itxJson.actualFee)), new BN(0));
|
|
173
|
+
|
|
174
|
+
const totalMissingFee = minRequiredReward.sub(totalFixedFee);
|
|
175
|
+
|
|
176
|
+
// 2. calculate actual reward for each dynamic reward tx, mark tx to be updated
|
|
177
|
+
const changes = { stake: [], account: [] };
|
|
178
|
+
dynamicFeeTxs.forEach((x) => {
|
|
179
|
+
const maxFee = new BN(x.tx.itxJson.maxFee);
|
|
180
|
+
const actualFee = totalMissingFee.mul(maxFee).div(totalDynamicFee);
|
|
181
|
+
|
|
182
|
+
// If the actualFee is less than the maxFee, user will have a refund
|
|
183
|
+
if (actualFee.lt(maxFee)) {
|
|
184
|
+
const refundFee = maxFee.sub(actualFee).toString(10);
|
|
185
|
+
changes.account.push({
|
|
186
|
+
address: x.tx.from,
|
|
187
|
+
delta: refundFee,
|
|
188
|
+
action: 'refund',
|
|
189
|
+
});
|
|
190
|
+
changes.stake.push({
|
|
191
|
+
address: locker,
|
|
192
|
+
delta: `-${refundFee}`,
|
|
193
|
+
action: 'refund',
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
x.tx.itxJson.actualFee = actualFee.toString(10);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// 3. bur/mint tokens, update stakes
|
|
201
|
+
txStates.forEach((x) => {
|
|
202
|
+
const user = x.tx.itxJson.token.value;
|
|
203
|
+
const fee = x.tx.itxJson.actualFee;
|
|
204
|
+
const total = getBNSum(user, fee);
|
|
205
|
+
|
|
206
|
+
if (x.type === 'deposit_token_v2') {
|
|
207
|
+
result.rewardAmount = result.rewardAmount.add(new BN(fee));
|
|
208
|
+
result.mintedAmount = result.mintedAmount.add(new BN(total));
|
|
209
|
+
|
|
210
|
+
// mint tokens for deposit proposer
|
|
211
|
+
changes.stake.push({
|
|
212
|
+
address: toStakeAddress(x.tx.itxJson.proposer, rollupAddress),
|
|
213
|
+
delta: total,
|
|
214
|
+
action: 'mint',
|
|
215
|
+
});
|
|
216
|
+
} else if (x.type === 'withdraw_token_v2') {
|
|
217
|
+
result.rewardAmount = result.rewardAmount.add(new BN(fee));
|
|
218
|
+
result.burnedAmount = result.burnedAmount.add(new BN(user));
|
|
219
|
+
|
|
220
|
+
// burn tokens from locked withdraws: user amount
|
|
221
|
+
changes.stake.push({
|
|
222
|
+
address: toStakeAddress(x.tx.from, rollupAddress),
|
|
223
|
+
delta: `-${user}`,
|
|
224
|
+
action: 'burn',
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
const grouped = {
|
|
230
|
+
stake: groupBy(changes.stake, 'address'),
|
|
231
|
+
account: groupBy(changes.account, 'address'),
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
result.stakeUpdates = Object.keys(grouped.stake).reduce((acc, x) => {
|
|
235
|
+
acc[x] = {
|
|
236
|
+
address: x,
|
|
237
|
+
token: tokenAddress,
|
|
238
|
+
delta: getBNSum(...grouped.stake[x].map((c) => c.delta)),
|
|
239
|
+
action: grouped.stake[x][grouped.stake[x].length - 1].action,
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
return acc;
|
|
243
|
+
}, {});
|
|
244
|
+
|
|
245
|
+
result.accountUpdates = Object.keys(grouped.account).reduce((acc, x) => {
|
|
246
|
+
acc[x] = {
|
|
247
|
+
address: x,
|
|
248
|
+
token: tokenAddress,
|
|
249
|
+
delta: getBNSum(...grouped.account[x].map((c) => c.delta)),
|
|
250
|
+
action: grouped.account[x][grouped.account[x].length - 1].action,
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
return acc;
|
|
254
|
+
}, {});
|
|
255
|
+
|
|
256
|
+
result.mintedAmount = result.mintedAmount.toString(10);
|
|
257
|
+
result.burnedAmount = result.burnedAmount.toString(10);
|
|
258
|
+
result.rewardAmount = result.rewardAmount.toString(10);
|
|
259
|
+
|
|
260
|
+
return result;
|
|
261
|
+
};
|
|
136
262
|
|
|
137
263
|
module.exports = {
|
|
138
264
|
decodeAnySafe,
|
|
139
265
|
applyTokenUpdates,
|
|
140
266
|
applyTokenChange,
|
|
141
267
|
fixTokenInput,
|
|
142
|
-
|
|
143
|
-
|
|
268
|
+
getTxFee,
|
|
269
|
+
splitTxFee,
|
|
270
|
+
getRewardLocker,
|
|
271
|
+
ensureBlockReward,
|
|
272
|
+
getBNSum,
|
|
273
|
+
isFixedFee,
|
|
274
|
+
RATE_BASE,
|
|
144
275
|
};
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.13.
|
|
6
|
+
"version": "1.13.61",
|
|
7
7
|
"description": "Predefined tx pipeline sets to execute certain type of transactions",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -19,16 +19,16 @@
|
|
|
19
19
|
"author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@arcblock/did": "1.13.
|
|
23
|
-
"@arcblock/did-util": "1.13.
|
|
24
|
-
"@ocap/asset": "1.13.
|
|
25
|
-
"@ocap/mcrypto": "1.13.
|
|
26
|
-
"@ocap/merkle-tree": "1.13.
|
|
27
|
-
"@ocap/message": "1.13.
|
|
28
|
-
"@ocap/state": "1.13.
|
|
29
|
-
"@ocap/tx-pipeline": "1.13.
|
|
30
|
-
"@ocap/util": "1.13.
|
|
31
|
-
"@ocap/wallet": "1.13.
|
|
22
|
+
"@arcblock/did": "1.13.61",
|
|
23
|
+
"@arcblock/did-util": "1.13.61",
|
|
24
|
+
"@ocap/asset": "1.13.61",
|
|
25
|
+
"@ocap/mcrypto": "1.13.61",
|
|
26
|
+
"@ocap/merkle-tree": "1.13.61",
|
|
27
|
+
"@ocap/message": "1.13.61",
|
|
28
|
+
"@ocap/state": "1.13.61",
|
|
29
|
+
"@ocap/tx-pipeline": "1.13.61",
|
|
30
|
+
"@ocap/util": "1.13.61",
|
|
31
|
+
"@ocap/wallet": "1.13.61",
|
|
32
32
|
"debug": "^4.3.2",
|
|
33
33
|
"empty-value": "^1.0.1",
|
|
34
34
|
"lodash": "^4.17.21",
|
|
@@ -41,5 +41,5 @@
|
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"jest": "^27.3.1"
|
|
43
43
|
},
|
|
44
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "79d07bbde4df1d0d37afe76aaa1eaa2f30cc55a7"
|
|
45
45
|
}
|