@ocap/tx-protocols 1.18.0 → 1.18.2
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/pipes/ensure-cost.js +27 -3
- package/lib/protocols/asset/acquire-v2.js +5 -1
- package/lib/protocols/governance/stake.js +35 -2
- package/lib/protocols/rollup/claim-reward.js +1 -1
- package/lib/protocols/token/withdraw-v2.js +3 -1
- package/lib/protocols/trade/exchange-v2.js +3 -1
- package/lib/protocols/trade/transfer-v2.js +3 -1
- package/lib/util.js +51 -1
- package/package.json +14 -13
package/lib/pipes/ensure-cost.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
const noop = require('lodash/noop');
|
|
2
2
|
const { CustomError: Error } = require('@ocap/util/lib/error');
|
|
3
3
|
const { fromTokenToUnit, BN } = require('@ocap/util');
|
|
4
|
+
const JWT = require('@arcblock/jwt');
|
|
4
5
|
const { account } = require('@ocap/state');
|
|
6
|
+
const { toAddress } = require('@arcblock/did');
|
|
7
|
+
const { toStakeAddress } = require('@arcblock/did-util');
|
|
5
8
|
|
|
6
9
|
// eslint-disable-next-line global-require
|
|
7
10
|
const debug = require('debug')(`${require('../../package.json').name}:pipes:ensure-cost`);
|
|
8
11
|
|
|
9
|
-
const { applyTokenUpdates } = require('../util');
|
|
12
|
+
const { applyTokenUpdates, isGasStakeValid } = require('../util');
|
|
10
13
|
|
|
11
14
|
// We have a layered transaction cost design
|
|
12
15
|
// - gas: charged for every tx for creating/updating states on the ledger
|
|
@@ -18,9 +21,9 @@ module.exports = function CreateEnsureTxCostPipe({ attachSenderChanges = true }
|
|
|
18
21
|
const { config, statedb, txType, senderState, gasEstimate, gasVaultState, totalGas } = context;
|
|
19
22
|
const feeVaultState = await statedb.account.get(config.vaults.txFee, context);
|
|
20
23
|
|
|
24
|
+
// Get service fee and gas fee
|
|
21
25
|
const changes = {};
|
|
22
26
|
let txCost = new BN(0);
|
|
23
|
-
|
|
24
27
|
const txFee = config.transaction.txFee[txType];
|
|
25
28
|
if (txFee) {
|
|
26
29
|
const totalFee = fromTokenToUnit(txFee, config.token.decimal);
|
|
@@ -34,7 +37,28 @@ module.exports = function CreateEnsureTxCostPipe({ attachSenderChanges = true }
|
|
|
34
37
|
context.gasVaultChange = changes.gas;
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
// verify gas staking headers
|
|
41
|
+
const { tx, gasStakeHeaders = {} } = context;
|
|
42
|
+
const { token, pk } = gasStakeHeaders || {};
|
|
43
|
+
const gasStake = {};
|
|
44
|
+
if (token && pk) {
|
|
45
|
+
if (JWT.verify(token, pk) === false) {
|
|
46
|
+
return next(new Error('INVALID_GAS_HEADER', 'Gas headers can not be verified'));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const decoded = JWT.decode(token);
|
|
50
|
+
gasStake.owner = toAddress(decoded.iss);
|
|
51
|
+
gasStake.stakeId = toStakeAddress(gasStake.owner, gasStake.owner);
|
|
52
|
+
} else {
|
|
53
|
+
gasStake.owner = tx.from;
|
|
54
|
+
gasStake.stakeId = toStakeAddress(gasStake.owner, gasStake.owner);
|
|
55
|
+
}
|
|
56
|
+
gasStake.state = await statedb.stake.get(gasStake.stakeId, context);
|
|
57
|
+
gasStake.valid = isGasStakeValid(gasStake.state, config);
|
|
58
|
+
context.gasStake = gasStake;
|
|
59
|
+
|
|
60
|
+
// If we have someone to pay for this tx
|
|
61
|
+
if (senderState && !gasStake.valid) {
|
|
38
62
|
const expected = new BN(gasEstimate.payment || 0).add(txCost);
|
|
39
63
|
const actual = new BN(senderState.tokens[config.token.address] || 0);
|
|
40
64
|
if (actual.lt(expected)) {
|
|
@@ -121,7 +121,11 @@ runner.use(
|
|
|
121
121
|
const senderUpdates = {
|
|
122
122
|
nonce: tx.nonce,
|
|
123
123
|
pk: tx.pk,
|
|
124
|
-
...applyTokenUpdates(
|
|
124
|
+
...applyTokenUpdates(
|
|
125
|
+
factoryTokens,
|
|
126
|
+
senderChange ? applyTokenChange(senderState, senderChange) : senderState,
|
|
127
|
+
'sub'
|
|
128
|
+
),
|
|
125
129
|
};
|
|
126
130
|
|
|
127
131
|
// Owner updates
|
|
@@ -3,6 +3,7 @@ const { promisify } = require('util');
|
|
|
3
3
|
const isEmpty = require('empty-value');
|
|
4
4
|
const { Joi } = require('@arcblock/validator');
|
|
5
5
|
const { CustomError: Error } = require('@ocap/util/lib/error');
|
|
6
|
+
const { BN, fromTokenToUnit } = require('@ocap/util');
|
|
6
7
|
const { Runner, pipes } = require('@ocap/tx-pipeline');
|
|
7
8
|
const { toStakeAddress } = require('@arcblock/did-util');
|
|
8
9
|
const { account, asset, stake } = require('@ocap/state');
|
|
@@ -10,7 +11,7 @@ const { account, asset, stake } = require('@ocap/state');
|
|
|
10
11
|
// eslint-disable-next-line global-require
|
|
11
12
|
const debug = require('debug')(`${require('../../../package.json').name}:stake`);
|
|
12
13
|
|
|
13
|
-
const { applyTokenUpdates, applyTokenChange } = require('../../util');
|
|
14
|
+
const { applyTokenUpdates, applyTokenChange, isGasStakeAddress, isGasStakeInput } = require('../../util');
|
|
14
15
|
|
|
15
16
|
const EnsureTxGas = require('../../pipes/ensure-gas');
|
|
16
17
|
const EnsureTxCost = require('../../pipes/ensure-cost');
|
|
@@ -97,6 +98,37 @@ runner.use(async (context, next) => {
|
|
|
97
98
|
return next();
|
|
98
99
|
});
|
|
99
100
|
|
|
101
|
+
// 8. handle tx gas stakes
|
|
102
|
+
runner.use(async (context, next) => {
|
|
103
|
+
const { tx, itx, inputs, config } = context;
|
|
104
|
+
const { token, transaction } = config;
|
|
105
|
+
const { minStake, maxStake } = transaction.txGas;
|
|
106
|
+
|
|
107
|
+
// FIXME: delegation not supported here
|
|
108
|
+
context.isGasStake = isGasStakeAddress(tx.from, itx.address) && isGasStakeInput(inputs, token.address);
|
|
109
|
+
|
|
110
|
+
if (context.isGasStake) {
|
|
111
|
+
const [tokenInput] = inputs[0].tokensList;
|
|
112
|
+
const actualStake = new BN(tokenInput.value);
|
|
113
|
+
const expectedMinStake = fromTokenToUnit(minStake, token.decimal);
|
|
114
|
+
const expectedMaxStake = fromTokenToUnit(maxStake, token.decimal);
|
|
115
|
+
if (actualStake.lt(expectedMinStake)) {
|
|
116
|
+
return next(new Error('INVALID_TX', `Stake for gas should be greater than: ${minStake}`));
|
|
117
|
+
}
|
|
118
|
+
if (actualStake.gt(expectedMaxStake)) {
|
|
119
|
+
return next(new Error('INVALID_TX', `Stake for gas should be less than: ${maxStake}`));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (context.isGasStake) {
|
|
124
|
+
context.revokeWaitingPeriod = transaction.txGas.stakeLockPeriod;
|
|
125
|
+
} else {
|
|
126
|
+
context.revokeWaitingPeriod = itx.revokeWaitingPeriod;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return next();
|
|
130
|
+
});
|
|
131
|
+
|
|
100
132
|
// Ensure tx fee and gas
|
|
101
133
|
runner.use(
|
|
102
134
|
EnsureTxGas((context) => {
|
|
@@ -192,7 +224,8 @@ runner.use(
|
|
|
192
224
|
{
|
|
193
225
|
sender: tx.from,
|
|
194
226
|
revocable: !itx.locked,
|
|
195
|
-
|
|
227
|
+
revokeWaitingPeriod: context.revokeWaitingPeriod,
|
|
228
|
+
...pick(itx, ['address', 'receiver', 'message', 'data']),
|
|
196
229
|
...stakeUpdates,
|
|
197
230
|
},
|
|
198
231
|
context
|
|
@@ -268,7 +268,7 @@ runner.use(
|
|
|
268
268
|
let updateSet = applyTokenChange(x, updates.account[x.address]);
|
|
269
269
|
if (x.address === senderState.address) {
|
|
270
270
|
updateSet.nonce = tx.nonce;
|
|
271
|
-
updateSet = Object.assign(updateSet, applyTokenChange(updateSet, senderChange));
|
|
271
|
+
updateSet = Object.assign(updateSet, senderChange ? applyTokenChange(updateSet, senderChange) : {});
|
|
272
272
|
}
|
|
273
273
|
|
|
274
274
|
return statedb.account.update(x.address, account.update(x, updateSet, context), context);
|
|
@@ -204,7 +204,9 @@ runner.use(
|
|
|
204
204
|
const fee = getBNSum(itx.actualFee, itx.maxFee);
|
|
205
205
|
|
|
206
206
|
let senderUpdates = applyTokenUpdates([{ address: itx.token.address, value: total }], senderState, 'sub');
|
|
207
|
-
|
|
207
|
+
if (senderChange) {
|
|
208
|
+
senderUpdates = applyTokenChange(senderUpdates, senderChange);
|
|
209
|
+
}
|
|
208
210
|
|
|
209
211
|
// Burned amount should equal to user received amount
|
|
210
212
|
const stakeUpdates = applyTokenUpdates(
|
|
@@ -174,7 +174,9 @@ runner.use(
|
|
|
174
174
|
receiverStateTokens[address] = new BN(receiverStateTokens[address] || '0').sub(delta).toString();
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
const senderUpdates =
|
|
177
|
+
const senderUpdates = senderChange
|
|
178
|
+
? applyTokenChange({ tokens: senderStateTokens }, senderChange)
|
|
179
|
+
: { tokens: senderStateTokens };
|
|
178
180
|
|
|
179
181
|
const [newSenderState, newReceiverState] = await Promise.all([
|
|
180
182
|
// Update sender state
|
|
@@ -128,7 +128,9 @@ runner.use(
|
|
|
128
128
|
receiverTokens[token.address] = new BN(receiverTokens[token.address] || '0').add(delta).toString();
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
const senderUpdates =
|
|
131
|
+
const senderUpdates = senderChange
|
|
132
|
+
? applyTokenChange({ tokens: senderTokens }, senderChange)
|
|
133
|
+
: { tokens: senderTokens };
|
|
132
134
|
|
|
133
135
|
const [newSenderState, newReceiverState] = await Promise.all([
|
|
134
136
|
// Update sender state
|
package/lib/util.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const groupBy = require('lodash/groupBy');
|
|
2
2
|
const flattenDeep = require('lodash/flattenDeep');
|
|
3
3
|
const { CustomError: Error } = require('@ocap/util/lib/error');
|
|
4
|
-
const { BN } = require('@ocap/util');
|
|
4
|
+
const { BN, fromTokenToUnit } = require('@ocap/util');
|
|
5
5
|
const { decodeAny } = require('@ocap/message');
|
|
6
6
|
const { toStakeAddress } = require('@arcblock/did-util');
|
|
7
7
|
const cloneDeep = require('lodash/cloneDeep');
|
|
@@ -313,6 +313,53 @@ const ensureBlockReward = (rollupState, minReward, txStates) => {
|
|
|
313
313
|
return result;
|
|
314
314
|
};
|
|
315
315
|
|
|
316
|
+
const isGasStakeAddress = (sender, stakeId) => toStakeAddress(sender, sender) === stakeId;
|
|
317
|
+
const isGasStakeInput = (inputs, token) => {
|
|
318
|
+
if (inputs.length !== 1) {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const [{ assetsList, tokensList }] = inputs;
|
|
323
|
+
if (assetsList.length > 0) {
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
if (tokensList.length !== 1) {
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const [tokenInput] = tokensList;
|
|
331
|
+
if (tokenInput.address !== token) {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return true;
|
|
336
|
+
};
|
|
337
|
+
const isGasStakeValid = (state, config) => {
|
|
338
|
+
if (!state) {
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const { token, transaction } = config;
|
|
343
|
+
const { minStake } = transaction.txGas;
|
|
344
|
+
const expectedMin = fromTokenToUnit(minStake, token.decimal);
|
|
345
|
+
|
|
346
|
+
if (state.tokens) {
|
|
347
|
+
const actualStaked = new BN(state.tokens[token.address] || 0);
|
|
348
|
+
if (actualStaked.gte(expectedMin)) {
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (state.revokedTokens) {
|
|
354
|
+
const actualRevoked = new BN(state.revokedTokens[token.address] || 0);
|
|
355
|
+
if (actualRevoked.gte(expectedMin)) {
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return false;
|
|
361
|
+
};
|
|
362
|
+
|
|
316
363
|
module.exports = {
|
|
317
364
|
decodeAnySafe,
|
|
318
365
|
applyTokenUpdates,
|
|
@@ -324,5 +371,8 @@ module.exports = {
|
|
|
324
371
|
ensureBlockReward,
|
|
325
372
|
getBNSum,
|
|
326
373
|
isFixedFee,
|
|
374
|
+
isGasStakeAddress,
|
|
375
|
+
isGasStakeInput,
|
|
376
|
+
isGasStakeValid,
|
|
327
377
|
RATE_BASE,
|
|
328
378
|
};
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.18.
|
|
6
|
+
"version": "1.18.2",
|
|
7
7
|
"description": "Predefined tx pipeline sets to execute certain type of transactions",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -21,17 +21,18 @@
|
|
|
21
21
|
"author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
|
|
22
22
|
"license": "MIT",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@arcblock/did": "1.18.
|
|
25
|
-
"@arcblock/did-util": "1.18.
|
|
26
|
-
"@arcblock/
|
|
27
|
-
"@
|
|
28
|
-
"@ocap/
|
|
29
|
-
"@ocap/
|
|
30
|
-
"@ocap/
|
|
31
|
-
"@ocap/
|
|
32
|
-
"@ocap/
|
|
33
|
-
"@ocap/
|
|
34
|
-
"@ocap/
|
|
24
|
+
"@arcblock/did": "1.18.2",
|
|
25
|
+
"@arcblock/did-util": "1.18.2",
|
|
26
|
+
"@arcblock/jwt": "1.18.2",
|
|
27
|
+
"@arcblock/validator": "1.18.2",
|
|
28
|
+
"@ocap/asset": "1.18.2",
|
|
29
|
+
"@ocap/mcrypto": "1.18.2",
|
|
30
|
+
"@ocap/merkle-tree": "1.18.2",
|
|
31
|
+
"@ocap/message": "1.18.2",
|
|
32
|
+
"@ocap/state": "1.18.2",
|
|
33
|
+
"@ocap/tx-pipeline": "1.18.2",
|
|
34
|
+
"@ocap/util": "1.18.2",
|
|
35
|
+
"@ocap/wallet": "1.18.2",
|
|
35
36
|
"debug": "^4.3.4",
|
|
36
37
|
"deep-diff": "^1.0.2",
|
|
37
38
|
"empty-value": "^1.0.1",
|
|
@@ -46,5 +47,5 @@
|
|
|
46
47
|
"jest": "^27.5.1",
|
|
47
48
|
"start-server-and-test": "^1.14.0"
|
|
48
49
|
},
|
|
49
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "7bd3abefcf732fcf5d4521622ca06db67671d853"
|
|
50
51
|
}
|