@ocap/tx-protocols 1.17.22 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/lib/pipes/ensure-cost.js +116 -0
  2. package/lib/pipes/ensure-gas.js +53 -0
  3. package/lib/protocols/account/delegate.js +30 -3
  4. package/lib/protocols/account/migrate.js +15 -1
  5. package/lib/protocols/account/revoke-delegate.js +14 -2
  6. package/lib/protocols/asset/acquire-v2.js +25 -2
  7. package/lib/protocols/asset/acquire-v3.js +34 -2
  8. package/lib/protocols/asset/create.js +9 -10
  9. package/lib/protocols/asset/mint.js +28 -2
  10. package/lib/protocols/asset/pipes/exec-mint-hook.js +1 -1
  11. package/lib/protocols/asset/update.js +11 -2
  12. package/lib/protocols/factory/create.js +8 -9
  13. package/lib/protocols/governance/claim-stake.js +41 -4
  14. package/lib/protocols/governance/revoke-stake.js +14 -3
  15. package/lib/protocols/governance/stake.js +53 -4
  16. package/lib/protocols/rollup/claim-reward.js +46 -16
  17. package/lib/protocols/rollup/create-block.js +35 -3
  18. package/lib/protocols/rollup/create.js +8 -18
  19. package/lib/protocols/rollup/join.js +25 -2
  20. package/lib/protocols/rollup/leave.js +15 -2
  21. package/lib/protocols/rollup/migrate-contract.js +13 -2
  22. package/lib/protocols/rollup/migrate-token.js +13 -2
  23. package/lib/protocols/rollup/pause.js +13 -2
  24. package/lib/protocols/rollup/resume.js +13 -2
  25. package/lib/protocols/rollup/update.js +13 -2
  26. package/lib/protocols/token/create.js +19 -9
  27. package/lib/protocols/token/deposit-v2.js +46 -6
  28. package/lib/protocols/token/withdraw-v2.js +43 -6
  29. package/lib/protocols/trade/exchange-v2.js +36 -3
  30. package/lib/protocols/trade/transfer-v2.js +27 -2
  31. package/lib/protocols/trade/transfer-v3.js +34 -1
  32. package/lib/util.js +7 -7
  33. package/package.json +13 -13
  34. package/lib/protocols/rollup/pipes/ensure-service-fee.js +0 -37
@@ -5,6 +5,8 @@ const { account, rollup } = require('@ocap/state');
5
5
 
6
6
  const VerifySigners = require('./pipes/verify-signers');
7
7
  const EnsureValidator = require('./pipes/ensure-validator');
8
+ const EnsureTxGas = require('../../pipes/ensure-gas');
9
+ const EnsureTxCost = require('../../pipes/ensure-cost');
8
10
 
9
11
  const runner = new Runner();
10
12
 
@@ -31,14 +33,23 @@ runner.use(pipes.ExtractState({ from: 'tx.from', to: 'senderState', status: 'INV
31
33
  runner.use(pipes.ExtractState({ from: 'signers', to: 'signerStates', status: 'INVALID_SIGNER_STATE', table: 'account' })); // prettier-ignore
32
34
  runner.use(pipes.VerifyAccountMigration({ signerKey: 'signerStates', senderKey: 'senderState' }));
33
35
 
36
+ // Ensure tx fee and gas
37
+ runner.use(EnsureTxGas(() => ({ create: 0, update: 2, payment: 0 })));
38
+ runner.use(EnsureTxCost({ attachSenderChanges: true }));
39
+
34
40
  // 5. update rollup state
35
41
  runner.use(
36
42
  async (context, next) => {
37
- const { tx, itx, rollupState, statedb, senderState } = context;
43
+ const { tx, itx, rollupState, statedb, senderState, senderUpdates, updateVaults } = context;
38
44
 
39
45
  const [newSenderState, newRollupState] = await Promise.all([
40
- statedb.account.update(senderState.address, account.update(senderState, { nonce: tx.nonce }, context), context),
46
+ statedb.account.update(
47
+ senderState.address,
48
+ account.update(senderState, { nonce: tx.nonce, ...senderUpdates }, context),
49
+ context
50
+ ),
41
51
  statedb.rollup.update(itx.rollup, rollup.pause(rollupState, context), context),
52
+ updateVaults(),
42
53
  ]);
43
54
 
44
55
  context.senderState = newSenderState;
@@ -5,6 +5,8 @@ const { account, rollup } = require('@ocap/state');
5
5
 
6
6
  const VerifySigners = require('./pipes/verify-signers');
7
7
  const EnsureValidator = require('./pipes/ensure-validator');
8
+ const EnsureTxGas = require('../../pipes/ensure-gas');
9
+ const EnsureTxCost = require('../../pipes/ensure-cost');
8
10
 
9
11
  const runner = new Runner();
10
12
 
@@ -31,14 +33,23 @@ runner.use(pipes.ExtractState({ from: 'tx.from', to: 'senderState', status: 'INV
31
33
  runner.use(pipes.ExtractState({ from: 'signers', to: 'signerStates', status: 'INVALID_SIGNER_STATE', table: 'account' })); // prettier-ignore
32
34
  runner.use(pipes.VerifyAccountMigration({ signerKey: 'signerStates', senderKey: 'senderState' }));
33
35
 
36
+ // Ensure tx fee and gas
37
+ runner.use(EnsureTxGas(() => ({ create: 0, update: 2, payment: 0 })));
38
+ runner.use(EnsureTxCost({ attachSenderChanges: true }));
39
+
34
40
  // 5. update rollup state
35
41
  runner.use(
36
42
  async (context, next) => {
37
- const { tx, itx, rollupState, statedb, senderState } = context;
43
+ const { tx, itx, rollupState, statedb, senderState, senderUpdates, updateVaults } = context;
38
44
 
39
45
  const [newSenderState, newRollupState] = await Promise.all([
40
- statedb.account.update(senderState.address, account.update(senderState, { nonce: tx.nonce }, context), context),
46
+ statedb.account.update(
47
+ senderState.address,
48
+ account.update(senderState, { nonce: tx.nonce, ...senderUpdates }, context),
49
+ context
50
+ ),
41
51
  statedb.rollup.update(itx.rollup, rollup.resume(rollupState, context), context),
52
+ updateVaults(),
42
53
  ]);
43
54
 
44
55
  context.senderState = newSenderState;
@@ -7,6 +7,8 @@ const { account, rollup } = require('@ocap/state');
7
7
  const debug = require('debug')(`${require('../../../package.json').name}:update-rollup`);
8
8
 
9
9
  const VerifySigners = require('./pipes/verify-signers');
10
+ const EnsureTxGas = require('../../pipes/ensure-gas');
11
+ const EnsureTxCost = require('../../pipes/ensure-cost');
10
12
 
11
13
  const { decodeAnySafe } = require('../../util');
12
14
 
@@ -74,15 +76,24 @@ runner.use(
74
76
  );
75
77
  runner.use(pipes.VerifyAccountMigration({ signerKey: 'signerStates', senderKey: 'senderState' }));
76
78
 
79
+ // Ensure tx fee and gas
80
+ runner.use(EnsureTxGas(() => ({ create: 0, update: 2, payment: 0 })));
81
+ runner.use(EnsureTxCost({ attachSenderChanges: true }));
82
+
77
83
  // 5. update rollup state
78
84
  runner.use(
79
85
  async (context, next) => {
80
- const { tx, itx, rollupState, statedb, senderState } = context;
86
+ const { tx, itx, rollupState, statedb, senderState, senderUpdates, updateVaults } = context;
81
87
  const data = decodeAnySafe(itx.data);
82
88
 
83
89
  const [newSenderState, newRollupState] = await Promise.all([
84
- statedb.account.update(senderState.address, account.update(senderState, { nonce: tx.nonce }, context), context),
90
+ statedb.account.update(
91
+ senderState.address,
92
+ account.update(senderState, { nonce: tx.nonce, ...senderUpdates }, context),
93
+ context
94
+ ),
85
95
  statedb.rollup.update(itx.rollup, rollup.update(rollupState, { ...itx, data }, context), context),
96
+ updateVaults(),
86
97
  ]);
87
98
 
88
99
  context.senderState = newSenderState;
@@ -10,7 +10,9 @@ const { fromTokenToUnit } = require('@ocap/util');
10
10
  // eslint-disable-next-line global-require
11
11
  const debug = require('debug')(`${require('../../../package.json').name}:create-token`);
12
12
  const { decodeAnySafe } = require('../../util');
13
- const ensureServiceFee = require('../rollup/pipes/ensure-service-fee');
13
+
14
+ const EnsureTxGas = require('../../pipes/ensure-gas');
15
+ const EnsureTxCost = require('../../pipes/ensure-cost');
14
16
 
15
17
  const MAX_TOTAL_SUPPLY = fromTokenToUnit(10000 * 100000000, 18); // 32
16
18
 
@@ -92,12 +94,23 @@ runner.use(async (context, next) => {
92
94
  return next();
93
95
  });
94
96
 
95
- runner.use(ensureServiceFee);
97
+ // Ensure tx fee and gas
98
+ runner.use(
99
+ EnsureTxGas((context) => {
100
+ const result = { create: 1, update: 1, payment: 0 };
101
+ if (context.delegatorState) {
102
+ result.update += 1;
103
+ }
104
+
105
+ return result;
106
+ })
107
+ );
108
+ runner.use(EnsureTxCost({ attachSenderChanges: true }));
96
109
 
97
110
  // Update sender state, token state
98
111
  runner.use(
99
112
  async (context, next) => {
100
- const { tx, itx, statedb, senderState, delegatorState, senderUpdates, vaultState, vaultUpdates } = context;
113
+ const { tx, itx, statedb, senderState, delegatorState, senderUpdates, updateVaults } = context;
101
114
  const data = decodeAnySafe(itx.data);
102
115
  const owner = delegatorState ? delegatorState.address : senderState.address;
103
116
 
@@ -113,7 +126,7 @@ runner.use(
113
126
  senderUpdates.tokens[itx.address] = itx.initialSupply;
114
127
  }
115
128
 
116
- const [newSenderState, tokenState, newVaultState, newDelegatorState] = await Promise.all([
129
+ const [newSenderState, tokenState, newDelegatorState] = await Promise.all([
117
130
  statedb.account.update(
118
131
  senderState.address,
119
132
  account.update(senderState, { nonce: tx.nonce, pk: tx.pk, ...senderUpdates }, context),
@@ -122,10 +135,6 @@ runner.use(
122
135
 
123
136
  statedb.token.create(itx.address, token.create({ ...cloneDeep(itx), data, issuer: owner }, context), context),
124
137
 
125
- isEmpty(vaultUpdates)
126
- ? vaultState
127
- : statedb.account.update(vaultState.address, account.update(vaultState, vaultUpdates, context), context),
128
-
129
138
  delegatorState
130
139
  ? statedb.account.update(
131
140
  delegatorState.address,
@@ -133,11 +142,12 @@ runner.use(
133
142
  context
134
143
  )
135
144
  : null,
145
+
146
+ updateVaults(),
136
147
  ]);
137
148
 
138
149
  context.senderState = newSenderState;
139
150
  context.tokenState = tokenState;
140
- context.vaultState = newVaultState;
141
151
  context.delegatorState = newDelegatorState;
142
152
 
143
153
  debug('create token v2', tokenState);
@@ -10,9 +10,12 @@ const { toStakeAddress } = require('@arcblock/did-util');
10
10
  // eslint-disable-next-line global-require
11
11
  const debug = require('debug')(`${require('../../../package.json').name}:deposit-token`);
12
12
 
13
+ const EnsureTxGas = require('../../pipes/ensure-gas');
14
+ const EnsureTxCost = require('../../pipes/ensure-cost');
15
+
13
16
  const VerifySigners = require('../rollup/pipes/verify-signers');
14
17
  const VerifyPaused = require('../rollup/pipes/verify-paused');
15
- const { applyTokenUpdates, getTxFee, getBNSum, getRewardLocker } = require('../../util');
18
+ const { applyTokenUpdates, applyTokenChange, getTxFee, getBNSum, getRewardLocker } = require('../../util');
16
19
 
17
20
  const schema = Joi.object({
18
21
  token: schemas.tokenInput.required(),
@@ -160,23 +163,58 @@ runner.use(pipes.ExtractState({ from: 'tx.from', to: 'senderState', status: 'OK'
160
163
  runner.use(pipes.ExtractState({ from: 'signers', to: 'signerStates', status: 'INVALID_SIGNER_STATE', table: 'account' })); // prettier-ignore
161
164
  runner.use(pipes.VerifyAccountMigration({ signerKey: 'signerStates', senderKey: 'senderState' }));
162
165
 
166
+ // Ensure tx fee and gas
167
+ runner.use(
168
+ EnsureTxGas((context) => {
169
+ // FIXME: payment check
170
+ const result = { create: 1, update: 1, payment: 0 };
171
+ if (context.senderState) {
172
+ result.update += 1;
173
+ } else {
174
+ result.create += 1;
175
+ }
176
+ if (context.lockerState) {
177
+ result.update += 1;
178
+ } else {
179
+ result.create += 1;
180
+ }
181
+ return result;
182
+ })
183
+ );
184
+ runner.use(EnsureTxCost({ attachSenderChanges: true }));
185
+
163
186
  // 8. update state: the token minting is done when deposit finalized in rollup-block
164
187
  runner.use(
165
188
  async (context, next) => {
166
- const { tx, itx, statedb, senderState, stakeState, stakeAddress, lockerState, lockerAddress } = context;
189
+ const {
190
+ tx,
191
+ itx,
192
+ statedb,
193
+ senderState,
194
+ stakeState,
195
+ stakeAddress,
196
+ lockerState,
197
+ lockerAddress,
198
+ senderChange,
199
+ updateVaults,
200
+ } = context;
167
201
 
168
202
  const user = itx.token.value;
169
203
  const fee = itx.actualFee;
170
204
  const total = getBNSum(user, fee);
171
205
 
172
206
  const stakeUpdates = applyTokenUpdates([{ address: itx.token.address, value: total }], stakeState, 'sub');
173
- const senderUpdates = applyTokenUpdates([{ address: itx.token.address, value: user }], senderState || {}, 'add');
207
+ let senderUpdates = applyTokenUpdates([{ address: itx.token.address, value: user }], senderState || {}, 'add');
174
208
  const lockerUpdates = applyTokenUpdates(
175
209
  [{ address: itx.token.address, value: fee }],
176
210
  lockerState || { tokens: {} },
177
211
  'add'
178
212
  );
179
213
 
214
+ if (senderChange && senderState) {
215
+ senderUpdates = applyTokenChange(senderUpdates, senderChange);
216
+ }
217
+
180
218
  const sender = senderState ? senderState.address : tx.from;
181
219
  const [newSenderState, newStakeState, newLockerState, evidenceState] = await Promise.all([
182
220
  // updateOrCreate user account
@@ -215,6 +253,8 @@ runner.use(
215
253
  evidence.create({ hash: itx.evidence.hash, data: 'rollup-deposit' }, context),
216
254
  context
217
255
  ),
256
+
257
+ updateVaults(),
218
258
  ]);
219
259
 
220
260
  context.senderState = newSenderState;
@@ -222,14 +262,14 @@ runner.use(
222
262
  context.evidenceState = evidenceState;
223
263
  context.stakeStates = [newStakeState, newLockerState];
224
264
 
225
- context.updatedAccounts = [
265
+ context.updatedAccounts.push(
226
266
  // stake for tx proposer is decreased
227
267
  { address: stakeAddress, token: itx.token.address, delta: `-${total}`, action: 'unlock' },
228
268
  // mint to depositor from stake
229
269
  { address: sender, token: itx.token.address, delta: user, action: 'unlock' },
230
270
  // tx fee is locked for later claiming
231
- { address: lockerAddress, token: itx.token.address, delta: fee, action: 'pending' },
232
- ];
271
+ { address: lockerAddress, token: itx.token.address, delta: fee, action: 'pending' }
272
+ );
233
273
 
234
274
  debug('deposit-token-v2', itx);
235
275
 
@@ -10,8 +10,11 @@ const { toStakeAddress } = require('@arcblock/did-util');
10
10
  // eslint-disable-next-line global-require
11
11
  const debug = require('debug')(`${require('../../../package.json').name}:withdraw-token`);
12
12
 
13
+ const EnsureTxGas = require('../../pipes/ensure-gas');
14
+ const EnsureTxCost = require('../../pipes/ensure-cost');
15
+
13
16
  const VerifyPaused = require('../rollup/pipes/verify-paused');
14
- const { applyTokenUpdates, getTxFee, getBNSum, getRewardLocker } = require('../../util');
17
+ const { applyTokenUpdates, getTxFee, getBNSum, getRewardLocker, applyTokenChange } = require('../../util');
15
18
 
16
19
  const verifyMultiSigV2 = pipes.VerifyMultiSigV2({ signersKey: 'signers' });
17
20
 
@@ -161,15 +164,47 @@ runner.use((context, next) => {
161
164
  return next();
162
165
  });
163
166
 
167
+ // Ensure tx fee and gas
168
+ runner.use(
169
+ EnsureTxGas((context) => {
170
+ // FIXME: payment check
171
+ const result = { create: 0, update: 1, payment: 0 };
172
+ if (context.stakeState) {
173
+ result.update += 1;
174
+ } else {
175
+ result.create += 1;
176
+ }
177
+ if (context.lockerState) {
178
+ result.update += 1;
179
+ } else {
180
+ result.create += 1;
181
+ }
182
+ return result;
183
+ })
184
+ );
185
+ runner.use(EnsureTxCost({ attachSenderChanges: true }));
186
+
164
187
  // 8. update state: the fee splitting and token burning is done when withdraw finalized in rollup-block
165
188
  runner.use(
166
189
  async (context, next) => {
167
- const { tx, itx, statedb, stakeState, senderState, lockerState, lockerAddress, stakeAddress } = context;
190
+ const {
191
+ tx,
192
+ itx,
193
+ statedb,
194
+ stakeState,
195
+ senderState,
196
+ lockerState,
197
+ lockerAddress,
198
+ stakeAddress,
199
+ senderChange,
200
+ updateVaults,
201
+ } = context;
168
202
 
169
203
  const total = getBNSum(itx.token.value, itx.actualFee, itx.maxFee);
170
204
  const fee = getBNSum(itx.actualFee, itx.maxFee);
171
205
 
172
- const senderUpdates = applyTokenUpdates([{ address: itx.token.address, value: total }], senderState, 'sub');
206
+ let senderUpdates = applyTokenUpdates([{ address: itx.token.address, value: total }], senderState, 'sub');
207
+ senderUpdates = applyTokenChange(senderUpdates, senderChange);
173
208
 
174
209
  // Burned amount should equal to user received amount
175
210
  const stakeUpdates = applyTokenUpdates(
@@ -232,6 +267,8 @@ runner.use(
232
267
  ),
233
268
  context
234
269
  ),
270
+
271
+ updateVaults(),
235
272
  ]);
236
273
 
237
274
  context.senderState = newSenderState;
@@ -239,11 +276,11 @@ runner.use(
239
276
  context.lockerState = newLockerState;
240
277
  context.stakeStates = [newStakeState, newLockerState];
241
278
 
242
- context.updatedAccounts = [
279
+ context.updatedAccounts.push(
243
280
  { address: senderState.address, token: itx.token.address, delta: `-${total}`, action: 'lock' },
244
281
  { address: stakeAddress, token: itx.token.address, delta: itx.token.value, action: 'lock' },
245
- { address: lockerAddress, token: itx.token.address, delta: fee, action: 'pending' },
246
- ];
282
+ { address: lockerAddress, token: itx.token.address, delta: fee, action: 'pending' }
283
+ );
247
284
 
248
285
  debug('withdraw-token-v2', itx);
249
286
 
@@ -9,6 +9,10 @@ const { account } = require('@ocap/state');
9
9
  // eslint-disable-next-line global-require
10
10
  const debug = require('debug')(`${require('../../../package.json').name}:exchange-v2`);
11
11
 
12
+ const EnsureTxGas = require('../../pipes/ensure-gas');
13
+ const EnsureTxCost = require('../../pipes/ensure-cost');
14
+ const { applyTokenChange } = require('../../util');
15
+
12
16
  const runner = new Runner();
13
17
 
14
18
  runner.use(pipes.VerifyMultiSig(1));
@@ -122,13 +126,38 @@ runner.use(pipes.ExtractState({ from: 'receiverAssets', to: 'priv.receiverAssets
122
126
  runner.use(pipes.VerifyTransferrable({ assets: 'priv.receiverAssets' }));
123
127
  runner.use(pipes.VerifyUpdater({ assetKey: 'priv.receiverAssets', ownerKey: 'receiverState' }));
124
128
 
129
+ // Ensure tx fee and gas
130
+ runner.use(
131
+ EnsureTxGas((context) => {
132
+ // FIXME: payment check
133
+ const ops = { create: 0, update: 2, payment: 0 };
134
+ ops.update += context.senderAssets.length;
135
+ ops.update += context.receiverAssets.length;
136
+
137
+ return ops;
138
+ })
139
+ );
140
+ runner.use(EnsureTxCost({ attachSenderChanges: true }));
141
+
125
142
  runner.use(pipes.UpdateOwner({ assets: 'priv.senderAssets', owner: 'receiverState' }));
126
143
  runner.use(pipes.UpdateOwner({ assets: 'priv.receiverAssets', owner: 'senderState' }));
127
144
 
145
+ // update statedb
128
146
  runner.use(
129
147
  async (context, next) => {
130
- const { tx, itx, senderAssets, receiverAssets, senderTokens, receiverTokens, senderState, receiverState, statedb } =
131
- context;
148
+ const {
149
+ tx,
150
+ itx,
151
+ senderAssets,
152
+ receiverAssets,
153
+ senderTokens,
154
+ receiverTokens,
155
+ senderState,
156
+ receiverState,
157
+ senderChange,
158
+ updateVaults,
159
+ statedb,
160
+ } = context;
132
161
 
133
162
  const senderStateTokens = senderState.tokens || {};
134
163
  const receiverStateTokens = receiverState.tokens || {};
@@ -145,11 +174,13 @@ runner.use(
145
174
  receiverStateTokens[address] = new BN(receiverStateTokens[address] || '0').sub(delta).toString();
146
175
  }
147
176
 
177
+ const senderUpdates = applyTokenChange({ tokens: senderStateTokens }, senderChange);
178
+
148
179
  const [newSenderState, newReceiverState] = await Promise.all([
149
180
  // Update sender state
150
181
  statedb.account.update(
151
182
  senderState.address,
152
- account.update(senderState, { nonce: tx.nonce, tokens: senderStateTokens }, context),
183
+ account.update(senderState, { nonce: tx.nonce, ...senderUpdates }, context),
153
184
  context
154
185
  ),
155
186
 
@@ -159,6 +190,8 @@ runner.use(
159
190
  account.update(receiverState, { tokens: receiverStateTokens }, context),
160
191
  context
161
192
  ),
193
+
194
+ updateVaults(),
162
195
  ]);
163
196
 
164
197
  context.senderState = newSenderState;
@@ -8,6 +8,10 @@ const { account } = require('@ocap/state');
8
8
  // eslint-disable-next-line global-require
9
9
  const debug = require('debug')(`${require('../../../package.json').name}:transfer-v2`);
10
10
 
11
+ const EnsureTxGas = require('../../pipes/ensure-gas');
12
+ const EnsureTxCost = require('../../pipes/ensure-cost');
13
+ const { applyTokenChange } = require('../../util');
14
+
11
15
  const runner = new Runner();
12
16
 
13
17
  runner.use(pipes.VerifyMultiSig(0));
@@ -86,6 +90,23 @@ runner.use(pipes.ExtractState({ from: 'assets', to: 'assetStates', status: 'INVA
86
90
  runner.use(pipes.VerifyTransferrable({ assets: 'assetStates' }));
87
91
  runner.use(pipes.VerifyUpdater({ assetKey: 'assetStates', ownerKey: 'senderState' }));
88
92
 
93
+ // Ensure tx fee and gas
94
+ runner.use(
95
+ EnsureTxGas((context) => {
96
+ // FIXME: payment check
97
+ const result = { create: 0, update: 1, payment: 0 };
98
+
99
+ if (context.receiverState) {
100
+ result.update += 1;
101
+ } else {
102
+ result.create += 1;
103
+ }
104
+
105
+ return result;
106
+ })
107
+ );
108
+ runner.use(EnsureTxCost({ attachSenderChanges: true }));
109
+
89
110
  // transfer assets to new owner
90
111
  runner.use((context, next) => {
91
112
  const { itx, receiverState } = context;
@@ -97,7 +118,7 @@ runner.use(pipes.UpdateOwner({ assets: 'assetStates', owner: 'receiverAddr' }));
97
118
  // update statedb: transfer tokens to new owner
98
119
  runner.use(
99
120
  async (context, next) => {
100
- const { tx, itx, tokens, senderState, receiverAddr, receiverState, statedb } = context;
121
+ const { tx, itx, tokens, senderState, receiverAddr, receiverState, statedb, senderChange, updateVaults } = context;
101
122
 
102
123
  const { tokens: senderTokens = {} } = senderState;
103
124
  const { tokens: receiverTokens = {} } = receiverState || {};
@@ -107,11 +128,13 @@ runner.use(
107
128
  receiverTokens[token.address] = new BN(receiverTokens[token.address] || '0').add(delta).toString();
108
129
  }
109
130
 
131
+ const senderUpdates = applyTokenChange({ tokens: senderTokens }, senderChange);
132
+
110
133
  const [newSenderState, newReceiverState] = await Promise.all([
111
134
  // Update sender state
112
135
  statedb.account.update(
113
136
  senderState.address,
114
- account.update(senderState, { nonce: tx.nonce, pk: tx.pk, tokens: senderTokens }, context),
137
+ account.update(senderState, { nonce: tx.nonce, pk: tx.pk, ...senderUpdates }, context),
115
138
  context
116
139
  ),
117
140
 
@@ -121,6 +144,8 @@ runner.use(
121
144
  account.updateOrCreate(receiverState, { address: receiverAddr, tokens: receiverTokens }, context),
122
145
  context
123
146
  ),
147
+
148
+ updateVaults(),
124
149
  ]);
125
150
 
126
151
  context.senderState = newSenderState;
@@ -11,7 +11,9 @@ const { getRelatedAddresses } = require('@ocap/util/lib/get-related-addr');
11
11
  // eslint-disable-next-line global-require
12
12
  const debug = require('debug')(`${require('../../../package.json').name}:transfer-v2`);
13
13
 
14
- const { applyTokenUpdates } = require('../../util');
14
+ const EnsureTxGas = require('../../pipes/ensure-gas');
15
+ const EnsureTxCost = require('../../pipes/ensure-cost');
16
+ const { applyTokenUpdates, applyTokenChange } = require('../../util');
15
17
 
16
18
  const runner = new Runner();
17
19
 
@@ -139,6 +141,27 @@ runner.use(async (context, next) => {
139
141
  return next();
140
142
  });
141
143
 
144
+ // Ensure tx fee and gas
145
+ runner.use(
146
+ EnsureTxGas((context) => {
147
+ // FIXME: payment check
148
+ const ops = { create: 0, update: 0, payment: 0 };
149
+ ops.update += context.signerStates.length;
150
+ ops.update += context.inputAssets.length;
151
+ ops.update += context.receivers.length;
152
+
153
+ if (context.senderState) {
154
+ ops.update += 1;
155
+ } else {
156
+ ops.create += 1;
157
+ }
158
+
159
+ return ops;
160
+ })
161
+ );
162
+ runner.use(EnsureTxCost({ attachSenderChanges: true }));
163
+
164
+ // Update statedb
142
165
  runner.use(
143
166
  async (context, next) => {
144
167
  const {
@@ -148,6 +171,8 @@ runner.use(
148
171
  receivers,
149
172
  senderState,
150
173
  signerStates,
174
+ senderChange,
175
+ updateVaults,
151
176
  receiverStates = [],
152
177
  assetStates = [],
153
178
  statedb,
@@ -161,6 +186,9 @@ runner.use(
161
186
  signerStates.find((s) => s.address === owner),
162
187
  'sub'
163
188
  );
189
+ if (senderChange && owner === senderChange.address) {
190
+ signerUpdates[owner] = applyTokenChange(signerUpdates[owner], senderChange);
191
+ }
164
192
  });
165
193
 
166
194
  const receiverUpdates = {};
@@ -169,6 +197,9 @@ runner.use(
169
197
  const { owner, tokensList, assetsList } = x;
170
198
  const ownerState = receiverStates.find((s) => getRelatedAddresses(s).includes(owner)) || { address: owner };
171
199
  receiverUpdates[ownerState.address] = applyTokenUpdates(tokensList, ownerState, 'add');
200
+ if (senderChange && ownerState.address === senderChange.address) {
201
+ receiverUpdates[ownerState.address] = applyTokenChange(receiverUpdates[ownerState.address], senderChange);
202
+ }
172
203
  assetsList.forEach((address) => {
173
204
  assetUpdates[address] = { owner: ownerState.address };
174
205
  });
@@ -224,6 +255,8 @@ runner.use(
224
255
  statedb.asset.update(x.address, asset.update(x, assetUpdates[x.address], context), context)
225
256
  )
226
257
  ),
258
+
259
+ updateVaults(),
227
260
  ]);
228
261
 
229
262
  context.senderState = newSenderState;
package/lib/util.js CHANGED
@@ -68,7 +68,7 @@ const applyTokenUpdates = (tokens, state, operator) => {
68
68
  const requirement = new BN(value);
69
69
  const balance = new BN(oldTokens[address] || 0);
70
70
  const newBalance = balance[operator](requirement);
71
- if (newBalance.lt(ZERO)) {
71
+ if (newBalance.isNeg()) {
72
72
  throw new Error('NEGATIVE_TOKEN_BALANCE', `Negative token balance when applyTokenUpdates for ${address}`);
73
73
  }
74
74
  newTokens[address] = newBalance.toString(10);
@@ -104,13 +104,13 @@ const getTxFee = ({ amount, feeRate, maxFee, minFee, stringify = true }) => {
104
104
  if (feeRate < 0) {
105
105
  throw new Error('NEGATIVE_FEE_RATE', 'Unexpected negative feeRate when getTxFee, abort!');
106
106
  }
107
- if (userAmount.lt(ZERO)) {
107
+ if (userAmount.isNeg()) {
108
108
  throw new Error('NEGATIVE_AMOUNT', 'Unexpected negative amount when getTxFee, abort!');
109
109
  }
110
- if (maxFeeAmount.lt(ZERO)) {
110
+ if (maxFeeAmount.isNeg()) {
111
111
  throw new Error('NEGATIVE_MAX_FEE', 'Unexpected negative maxFee when getTxFee, abort!');
112
112
  }
113
- if (minFeeAmount.lt(ZERO)) {
113
+ if (minFeeAmount.isNeg()) {
114
114
  throw new Error('NEGATIVE_MIN_FEE', 'Unexpected negative minFee when getTxFee, abort!');
115
115
  }
116
116
 
@@ -143,7 +143,7 @@ const getTxFee = ({ amount, feeRate, maxFee, minFee, stringify = true }) => {
143
143
 
144
144
  const splitTxFee = ({ total, shares = {}, stringify = true }) => {
145
145
  const totalAmount = new BN(total);
146
- if (totalAmount.lt(ZERO)) {
146
+ if (totalAmount.isNeg()) {
147
147
  throw new Error('NEGATIVE_TOTAL_AMOUNT', 'Unexpected negative total when splitTxFee, abort!');
148
148
  }
149
149
  Object.keys(shares).forEach((key) => {
@@ -211,14 +211,14 @@ const ensureBlockReward = (rollupState, minReward, txStates) => {
211
211
 
212
212
  let actualFee = new BN(0);
213
213
  // If totalMissingFee is less than 0, then the tx will be charged for fixedFee
214
- if (totalMissingFee.lt(ZERO)) {
214
+ if (totalMissingFee.isNeg()) {
215
215
  actualFee = defaults.reward;
216
216
  } else {
217
217
  // Else the tx is charged for a portion of totalMissingFee
218
218
  actualFee = totalMissingFee.mul(maxFee).div(totalDynamicFee);
219
219
  }
220
220
 
221
- if (actualFee.lt(ZERO)) {
221
+ if (actualFee.isNeg()) {
222
222
  throw new Error('NEGATIVE_ACTUAL_FEE', 'Got negative actualFee for tx, abort!');
223
223
  }
224
224
 
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.17.22",
6
+ "version": "1.18.0",
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,17 @@
21
21
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
22
22
  "license": "MIT",
23
23
  "dependencies": {
24
- "@arcblock/did": "1.17.22",
25
- "@arcblock/did-util": "1.17.22",
26
- "@arcblock/validator": "1.17.22",
27
- "@ocap/asset": "1.17.22",
28
- "@ocap/mcrypto": "1.17.22",
29
- "@ocap/merkle-tree": "1.17.22",
30
- "@ocap/message": "1.17.22",
31
- "@ocap/state": "1.17.22",
32
- "@ocap/tx-pipeline": "1.17.22",
33
- "@ocap/util": "1.17.22",
34
- "@ocap/wallet": "1.17.22",
24
+ "@arcblock/did": "1.18.0",
25
+ "@arcblock/did-util": "1.18.0",
26
+ "@arcblock/validator": "1.18.0",
27
+ "@ocap/asset": "1.18.0",
28
+ "@ocap/mcrypto": "1.18.0",
29
+ "@ocap/merkle-tree": "1.18.0",
30
+ "@ocap/message": "1.18.0",
31
+ "@ocap/state": "1.18.0",
32
+ "@ocap/tx-pipeline": "1.18.0",
33
+ "@ocap/util": "1.18.0",
34
+ "@ocap/wallet": "1.18.0",
35
35
  "debug": "^4.3.4",
36
36
  "deep-diff": "^1.0.2",
37
37
  "empty-value": "^1.0.1",
@@ -46,5 +46,5 @@
46
46
  "jest": "^27.5.1",
47
47
  "start-server-and-test": "^1.14.0"
48
48
  },
49
- "gitHead": "f6cd2a2ca2a417081d0be22f86547195b0e2bc14"
49
+ "gitHead": "c48f928ee4f0deddf0f5e4bcb82fd6ffd7f2bc99"
50
50
  }