@ocap/tx-protocols 1.6.3 → 1.6.10

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 (52) hide show
  1. package/README.md +36 -0
  2. package/lib/execute.js +97 -30
  3. package/lib/index.js +56 -14
  4. package/lib/protocols/account/declare.js +36 -30
  5. package/lib/protocols/account/delegate.js +78 -40
  6. package/lib/protocols/account/migrate.js +65 -49
  7. package/lib/protocols/account/revoke-delegate.js +39 -23
  8. package/lib/protocols/asset/acquire-v2.js +159 -0
  9. package/lib/protocols/asset/acquire-v3.js +242 -0
  10. package/lib/protocols/asset/calls/README.md +5 -0
  11. package/lib/protocols/asset/calls/transfer-token.js +37 -0
  12. package/lib/protocols/asset/calls/transfer.js +29 -0
  13. package/lib/protocols/asset/create.js +85 -36
  14. package/lib/protocols/asset/mint.js +133 -0
  15. package/lib/protocols/asset/pipes/exec-mint-hook.js +59 -0
  16. package/lib/protocols/asset/pipes/extract-factory-tokens.js +18 -0
  17. package/lib/protocols/asset/pipes/verify-acquire-params.js +30 -0
  18. package/lib/protocols/asset/pipes/verify-itx-address.js +41 -0
  19. package/lib/protocols/asset/pipes/verify-itx-assets.js +49 -0
  20. package/lib/protocols/asset/pipes/verify-itx-variables.js +26 -0
  21. package/lib/protocols/asset/pipes/verify-mint-limit.js +13 -0
  22. package/lib/protocols/asset/update.js +76 -44
  23. package/lib/protocols/factory/create.js +146 -0
  24. package/lib/protocols/governance/claim-stake.js +219 -0
  25. package/lib/protocols/governance/revoke-stake.js +136 -0
  26. package/lib/protocols/governance/stake.js +176 -0
  27. package/lib/protocols/rollup/claim-reward.js +283 -0
  28. package/lib/protocols/rollup/create-block.js +333 -0
  29. package/lib/protocols/rollup/create.js +169 -0
  30. package/lib/protocols/rollup/join.js +156 -0
  31. package/lib/protocols/rollup/leave.js +127 -0
  32. package/lib/protocols/rollup/migrate-contract.js +53 -0
  33. package/lib/protocols/rollup/migrate-token.js +66 -0
  34. package/lib/protocols/rollup/pause.js +52 -0
  35. package/lib/protocols/rollup/pipes/ensure-service-fee.js +37 -0
  36. package/lib/protocols/rollup/pipes/ensure-validator.js +10 -0
  37. package/lib/protocols/rollup/pipes/verify-evidence.js +37 -0
  38. package/lib/protocols/rollup/pipes/verify-paused.js +10 -0
  39. package/lib/protocols/rollup/pipes/verify-signers.js +88 -0
  40. package/lib/protocols/rollup/resume.js +52 -0
  41. package/lib/protocols/rollup/update.js +98 -0
  42. package/lib/protocols/token/create.js +150 -0
  43. package/lib/protocols/token/deposit-v2.js +241 -0
  44. package/lib/protocols/token/withdraw-v2.js +255 -0
  45. package/lib/protocols/trade/exchange-v2.js +179 -0
  46. package/lib/protocols/trade/transfer-v2.js +136 -0
  47. package/lib/protocols/trade/transfer-v3.js +241 -0
  48. package/lib/util.js +325 -2
  49. package/package.json +23 -16
  50. package/lib/protocols/misc/poke.js +0 -106
  51. package/lib/protocols/trade/exchange.js +0 -139
  52. package/lib/protocols/trade/transfer.js +0 -101
@@ -1,5 +1,6 @@
1
1
  const get = require('lodash/get');
2
- const isEmpty = require('empty-value');
2
+ const cloneDeep = require('lodash/cloneDeep');
3
+ const Joi = require('@arcblock/validator');
3
4
  const getListField = require('@ocap/util/lib/get-list-field');
4
5
  const { Runner, pipes } = require('@ocap/tx-pipeline');
5
6
  const { delegation, account } = require('@ocap/state');
@@ -13,6 +14,19 @@ const runner = new Runner();
13
14
  runner.use(pipes.VerifyMultiSig(0));
14
15
 
15
16
  // Verify itx
17
+ const schema = Joi.object({
18
+ address: Joi.DID().role('ROLE_DELEGATION').required(),
19
+ to: Joi.DID().required(),
20
+ typeUrlsList: Joi.array().items(Joi.string().required()).min(1).required(),
21
+ data: Joi.any().optional(),
22
+ }).options({ stripUnknown: true, noDefaults: false });
23
+ runner.use(({ itx }, next) => {
24
+ const { error } = schema.validate(itx);
25
+ if (error) {
26
+ return next(new Error('INVALID_TX', `Invalid itx: ${error.message}`));
27
+ }
28
+ return next();
29
+ });
16
30
  runner.use(
17
31
  pipes.VerifyInfo([
18
32
  {
@@ -22,11 +36,6 @@ runner.use(
22
36
  return true;
23
37
  },
24
38
  },
25
- {
26
- error: 'INSUFFICIENT_DATA',
27
- message: 'itx.address, itx.to and itx.typeUrls should not be empty',
28
- fn: ({ itx, typeUrls }) => !isEmpty(itx.address) && !isEmpty(itx.to) && typeUrls.length > 0,
29
- },
30
39
  {
31
40
  error: 'INVALID_TX',
32
41
  message: 'Delegation address does not match',
@@ -45,29 +54,36 @@ runner.use(
45
54
  );
46
55
 
47
56
  // Ensure sender/receiver/delegation exist
48
- runner.use(pipes.ExtractState({ from: 'tx.from', to: 'senderState', status: 'INVALID_SENDER_STATE' }));
57
+ runner.use(pipes.ExtractState({ from: 'itx.address', to: 'delegationState', status: 'INVALID_DELEGATION', table: 'delegation' })); // prettier-ignore
58
+ runner.use(pipes.ExtractState({ from: 'tx.from', to: 'senderState', status: 'INVALID_SENDER_STATE', table: 'account' })); // prettier-ignore
49
59
  runner.use(pipes.VerifyAccountMigration({ senderKey: 'senderState' }));
50
- runner.use(pipes.ExtractState({ from: 'itx.to', to: 'receiverState', status: 'INVALID_RECEIVER_STATE' }));
51
- runner.use(pipes.ExtractState({ from: 'itx.address', to: 'delegationState', status: 'INVALID_DELEGATION' }));
52
60
 
53
- // Create account state, and update old accounts
54
- runner.use(async (context, next) => {
55
- const { itx, typeUrls, statedb, senderState } = context;
61
+ // Update delegation state
62
+ runner.use(
63
+ async (context, next) => {
64
+ const { tx, itx, typeUrls, statedb, senderState, delegationState } = context;
56
65
 
57
- debug('update', { address: itx.address, typeUrls });
66
+ debug('update', { address: itx.address, typeUrls });
58
67
 
59
- typeUrls.forEach((x) => delete context.delegationState.ops[x]);
60
- const state = delegation.update(context.delegationState, { ops: context.delegationState.ops }, context);
61
- await statedb.delegation.update(itx.address, state, context);
68
+ const newOps = cloneDeep(delegationState.ops);
69
+ typeUrls.forEach((x) => delete newOps[x]);
62
70
 
63
- const newSenderState = account.update(senderState, {}, context);
64
- await statedb.account.update(senderState.address, newSenderState, context);
71
+ const [newSenderState, newDelegationState] = await Promise.all([
72
+ statedb.account.update(senderState.address, account.update(senderState, { nonce: tx.nonce }, context), context),
73
+ statedb.delegation.update(
74
+ itx.address,
75
+ delegation.update(context.delegationState, { ops: newOps }, context),
76
+ context
77
+ ),
78
+ ]);
65
79
 
66
- // Update context
67
- context.delegationState = state;
68
- context.senderState = newSenderState;
80
+ // Update context
81
+ context.senderState = newSenderState;
82
+ context.delegationState = newDelegationState;
69
83
 
70
- return next();
71
- });
84
+ return next();
85
+ },
86
+ { persistError: true }
87
+ );
72
88
 
73
89
  module.exports = runner;
@@ -0,0 +1,159 @@
1
+ /* eslint-disable function-paren-newline */
2
+ const { promisify } = require('util');
3
+ const { Runner, pipes } = require('@ocap/tx-pipeline');
4
+ const { account, asset, factory } = require('@ocap/state');
5
+
6
+ // eslint-disable-next-line global-require
7
+ const debug = require('debug')(`${require('../../../package.json').name}:acquire-asset-v2`);
8
+
9
+ // custom pipes
10
+ const verifyAcquireParams = require('./pipes/verify-acquire-params');
11
+ const verifyMintLimit = require('./pipes/verify-mint-limit');
12
+ const verifyItxVariables = require('./pipes/verify-itx-variables');
13
+ const verifyItxAssets = require('./pipes/verify-itx-assets');
14
+ const verifyItxAddress = require('./pipes/verify-itx-address');
15
+ const execMintHook = require('./pipes/exec-mint-hook');
16
+ const extractFactoryTokens = require('./pipes/extract-factory-tokens');
17
+
18
+ const { applyTokenUpdates } = require('../../util');
19
+
20
+ const verifyTokenBalance = promisify(pipes.VerifyTokenBalance({ ownerKey: 'senders', conditionKey: 'conditions' }));
21
+
22
+ const runner = new Runner();
23
+
24
+ runner.use(pipes.VerifyMultiSig(0));
25
+ runner.use(verifyAcquireParams('acquire-v2'));
26
+
27
+ // ensure asset not exist
28
+ runner.use(pipes.ExtractState({ from: 'itx.address', to: 'duplicateAsset', status: 'OK', table: 'asset' }));
29
+ runner.use(
30
+ pipes.VerifyInfo([
31
+ {
32
+ error: 'FORBIDDEN',
33
+ message: 'Asset with same address already exists',
34
+ fn: ({ duplicateAsset }) => !duplicateAsset,
35
+ persist: true,
36
+ },
37
+ ])
38
+ );
39
+
40
+ runner.use(pipes.ExtractState({ from: 'tx.from', to: 'senderState', status: 'INVALID_SENDER_STATE', table: 'account' })); // prettier-ignore
41
+ runner.use(pipes.VerifyAccountMigration({ senderKey: 'senderState' }));
42
+
43
+ runner.use(pipes.ExtractState({ from: 'tx.delegator', to: 'delegatorState', status: 'OK' }));
44
+ runner.use(pipes.VerifyDelegation({ type: 'signature', signerKey: 'senderState', delegatorKey: 'delegatorState' }));
45
+
46
+ runner.use(pipes.ExtractState({ from: 'itx.factory', to: 'factoryState', status: 'INVALID_FACTORY_STATE', table: 'factory' })); // prettier-ignore
47
+ runner.use(pipes.ExtractState({ from: 'itx.assetsList', to: 'assetStates', status: 'OK', table: 'asset' }));
48
+ runner.use(
49
+ pipes.ExtractState({
50
+ from: 'factoryState.input.assets',
51
+ to: 'expectedFactoryStates',
52
+ status: 'OK',
53
+ table: 'factory',
54
+ })
55
+ );
56
+
57
+ // Though tokens are verified on factory creating phase, we need them here to construct tokenSymbols
58
+ runner.use(extractFactoryTokens);
59
+ runner.use(pipes.ExtractState({ from: 'tokenAddress', to: 'tokenStates', status: 'INVALID_TOKEN', table: 'token' }));
60
+
61
+ // Verify itx
62
+ runner.use(verifyMintLimit);
63
+ runner.use(verifyItxVariables);
64
+ runner.use(verifyItxAddress('acquire-v2'));
65
+ runner.use(async (context, next) => {
66
+ const { factoryTokens, senderState } = context;
67
+ const condition = {
68
+ owner: senderState.address,
69
+ tokens: [...factoryTokens],
70
+ };
71
+
72
+ try {
73
+ await verifyTokenBalance({ senders: [senderState], conditions: [condition] });
74
+ return next();
75
+ } catch (err) {
76
+ return next(err);
77
+ }
78
+ });
79
+ runner.use(pipes.VerifyUpdater({ assetKey: 'assetStates', ownerKey: 'assetOwner' }));
80
+ runner.use(verifyItxAssets);
81
+
82
+ // update statedb
83
+ runner.use(
84
+ async (context, next) => {
85
+ const {
86
+ tx,
87
+ itx,
88
+ txTime,
89
+ statedb,
90
+ senderState,
91
+ assetOwner,
92
+ factoryState,
93
+ factoryTokens,
94
+ mintedAsset,
95
+ mintedAddress,
96
+ assetStates = [],
97
+ } = context;
98
+
99
+ // Sender updates
100
+ const senderUpdates = {
101
+ nonce: tx.nonce,
102
+ pk: tx.pk,
103
+ ...applyTokenUpdates(factoryTokens, senderState, 'sub'),
104
+ };
105
+
106
+ // Owner updates
107
+ const ownerUpdates = {};
108
+
109
+ // Factory updates
110
+ const factoryUpdates = { numMinted: factoryState.numMinted + 1 };
111
+ if (factoryState.settlement !== 'instant') {
112
+ // Move primary/secondary tokens to factory if we are not doing instant settlement
113
+ Object.assign(factoryUpdates, applyTokenUpdates(factoryTokens, factoryState, 'add'));
114
+ context.tokenUpdates = applyTokenUpdates(factoryTokens, { tokens: {} }, 'add');
115
+ }
116
+
117
+ const [newSenderState, assetState, newFactoryState, newAssetStates] = await Promise.all([
118
+ // Update sender state
119
+ statedb.account.update(senderState.address, account.update(senderState, senderUpdates, context), context),
120
+
121
+ // Create new asset
122
+ statedb.asset.create(
123
+ itx.address,
124
+ asset.create({ ...mintedAsset, owner: assetOwner.address, address: mintedAddress }, context),
125
+ context
126
+ ),
127
+
128
+ // Update factory state
129
+ statedb.factory.update(factoryState.address, factory.update(factoryState, factoryUpdates, context), context),
130
+
131
+ // Mark input assets as consumed
132
+ Promise.all(
133
+ assetStates.map((x) =>
134
+ statedb.asset.update(x.address, asset.update(x, { consumedTime: txTime }, context), context)
135
+ )
136
+ ),
137
+
138
+ // Update delegator state
139
+ context.delegatorState
140
+ ? statedb.account.update(assetOwner.address, account.update(assetOwner, ownerUpdates, context), context)
141
+ : null,
142
+ ]);
143
+
144
+ context.senderState = newSenderState;
145
+ context.assetState = assetState;
146
+ context.factoryState = newFactoryState;
147
+ context.assetStates = newAssetStates;
148
+
149
+ debug('acquire-v2', assetState);
150
+
151
+ next();
152
+ },
153
+ { persistError: true }
154
+ );
155
+
156
+ // run minting hooks for instant settlement
157
+ runner.use(execMintHook, { persistError: true });
158
+
159
+ module.exports = runner;
@@ -0,0 +1,242 @@
1
+ /* eslint-disable no-restricted-syntax */
2
+ /* eslint-disable prefer-object-spread */
3
+ /* eslint-disable function-paren-newline */
4
+ const { promisify } = require('util');
5
+ const isEqual = require('lodash/isEqual');
6
+ const { BN } = require('@ocap/util');
7
+ const { Runner, pipes } = require('@ocap/tx-pipeline');
8
+ const { account, asset, factory } = require('@ocap/state');
9
+
10
+ // eslint-disable-next-line global-require
11
+ const debug = require('debug')(`${require('../../../package.json').name}:acquire-asset-v2`);
12
+
13
+ const { applyTokenUpdates } = require('../../util');
14
+
15
+ // custom pipes
16
+ const verifyAcquireParams = require('./pipes/verify-acquire-params');
17
+ const verifyMintLimit = require('./pipes/verify-mint-limit');
18
+ const verifyItxVariables = require('./pipes/verify-itx-variables');
19
+ const verifyItxAssets = require('./pipes/verify-itx-assets');
20
+ const verifyItxAddress = require('./pipes/verify-itx-address');
21
+ const execMintHook = require('./pipes/exec-mint-hook');
22
+ const extractFactoryTokens = require('./pipes/extract-factory-tokens');
23
+
24
+ const verifyAssetOwner = promisify(pipes.VerifyUpdater({ assetKey: 'assets', ownerKey: 'owner' }));
25
+
26
+ const runner = new Runner();
27
+
28
+ // 1. extract & verify itx input
29
+ runner.use(verifyAcquireParams('acquire-v3'));
30
+
31
+ // ensure asset not exist
32
+ runner.use(pipes.ExtractState({ from: 'itx.address', to: 'duplicateAsset', status: 'OK', table: 'asset' }));
33
+ runner.use(
34
+ pipes.VerifyInfo([
35
+ {
36
+ error: 'FORBIDDEN',
37
+ message: 'Asset with same address already exists',
38
+ fn: ({ duplicateAsset }) => !duplicateAsset,
39
+ persist: true,
40
+ },
41
+ ])
42
+ );
43
+ runner.use(
44
+ pipes.VerifyTxInput({
45
+ fieldKey: 'itx.inputs',
46
+ inputsKey: 'inputs',
47
+ sendersKey: 'senders',
48
+ tokensKey: 'tokens',
49
+ assetsKey: 'assets',
50
+ })
51
+ );
52
+
53
+ // 2. verify itx size: set hard limit here because more inputs leads to longer tx execute time
54
+ runner.use(pipes.VerifyListSize({ listKey: ['inputs', 'senders', 'tokens', 'assets'] }));
55
+
56
+ // 3. verify multi sig
57
+ runner.use(pipes.VerifyMultiSigV2({ signersKey: 'senders' }));
58
+
59
+ // 4. verify against factory state
60
+ runner.use(pipes.ExtractState({ from: 'itx.factory', to: 'factoryState', status: 'INVALID_FACTORY_STATE', table: 'factory' })); // prettier-ignore
61
+ runner.use(extractFactoryTokens);
62
+ runner.use(verifyMintLimit);
63
+ runner.use(verifyItxVariables);
64
+ runner.use(
65
+ pipes.VerifyInfo([
66
+ {
67
+ error: 'INVALID_TX',
68
+ message: 'Input token address does not match with factory input requirement',
69
+ fn: (ctx) => isEqual(ctx.tokens, ctx.tokenAddress),
70
+ },
71
+ {
72
+ error: 'INVALID_TX',
73
+ message: 'Input token amount does not match with factory input requirement',
74
+ fn: ({ inputs, factoryTokens }) => {
75
+ const inputMap = {};
76
+
77
+ inputs.forEach(({ tokensList }) => {
78
+ tokensList.forEach(({ address, value }) => {
79
+ if (typeof inputMap[address] === 'undefined') {
80
+ inputMap[address] = new BN(0);
81
+ }
82
+ inputMap[address] = inputMap[address].add(new BN(value));
83
+ });
84
+ });
85
+
86
+ const expectedTokens = factoryTokens.reduce((acc, x) => {
87
+ acc[x.address] = new BN(x.value);
88
+ return acc;
89
+ }, {});
90
+
91
+ return Object.keys(expectedTokens).every((address) => inputMap[address].eq(expectedTokens[address]));
92
+ },
93
+ },
94
+ ])
95
+ );
96
+
97
+ // 5. verify sender & signer & owner
98
+ runner.use(pipes.ExtractState({ from: 'tx.from', to: 'senderState', status: 'OK', table: 'account' })); // prettier-ignore
99
+ runner.use(pipes.ExtractState({ from: 'senders', to: 'signerStates', status: 'INVALID_SIGNER_STATE', table: 'account' })); // prettier-ignore
100
+ runner.use(pipes.ExtractState({ from: 'itx.owner', to: 'ownerState', status: 'OK', table: 'account' }));
101
+ runner.use(pipes.VerifyAccountMigration({ signerKey: 'signerStates', senderKey: 'senderState' }));
102
+
103
+ // 6. verify token state and balance
104
+ runner.use(pipes.ExtractState({ from: 'tokens', to: 'tokenStates', status: 'INVALID_TOKEN', table: 'token' }));
105
+ runner.use(pipes.VerifyTokenBalance({ ownerKey: 'signerStates', conditionKey: 'inputs' }));
106
+
107
+ // 7. verify asset state and ownership
108
+ runner.use(pipes.ExtractState({ from: 'assets', to: 'assetStates', status: 'OK', table: 'asset' }));
109
+ runner.use(async (context, next) => {
110
+ const { inputs, assetStates = [], signerStates } = context;
111
+ for (const input of inputs) {
112
+ const { owner, assetsList } = input;
113
+ const states = assetStates.filter((x) => assetsList.includes(x.address));
114
+ const signer = signerStates.find((x) => x.address === owner);
115
+
116
+ try {
117
+ // eslint-disable-next-line no-await-in-loop
118
+ await verifyAssetOwner({ assets: states, owner: signer });
119
+ } catch (err) {
120
+ return next(err);
121
+ }
122
+ }
123
+
124
+ return next();
125
+ });
126
+ runner.use(
127
+ pipes.ExtractState({
128
+ from: 'factoryState.input.assets',
129
+ to: 'expectedFactoryStates',
130
+ status: 'OK',
131
+ table: 'factory',
132
+ })
133
+ );
134
+
135
+ // Verify itx
136
+ runner.use(verifyItxAddress('acquire-v3'));
137
+ runner.use(verifyItxAssets);
138
+
139
+ // update statedb
140
+ runner.use(
141
+ async (context, next) => {
142
+ const {
143
+ tx,
144
+ itx,
145
+ txTime,
146
+ inputs,
147
+ statedb,
148
+ senderState,
149
+ ownerState,
150
+ factoryState,
151
+ factoryTokens,
152
+ mintedAsset,
153
+ mintedAddress,
154
+ signerStates,
155
+ assetStates = [],
156
+ } = context;
157
+
158
+ const signerUpdates = {};
159
+ inputs.forEach((x) => {
160
+ const { owner, tokensList } = x;
161
+ signerUpdates[owner] = applyTokenUpdates(
162
+ tokensList,
163
+ signerStates.find((s) => s.address === owner),
164
+ 'sub'
165
+ );
166
+ });
167
+
168
+ const isAlsoSigner = !!signerUpdates[tx.from];
169
+ const owner = ownerState ? ownerState.address : itx.owner;
170
+
171
+ // Factory updates
172
+ const factoryUpdates = { numMinted: factoryState.numMinted + 1 };
173
+
174
+ // Move primary/secondary tokens to factory if we are not doing instant settlement
175
+ if (factoryState.settlement !== 'instant') {
176
+ Object.assign(factoryUpdates, applyTokenUpdates(factoryTokens, factoryState, 'add'));
177
+ context.tokenUpdates = applyTokenUpdates(factoryTokens, { tokens: {} }, 'add');
178
+ }
179
+
180
+ const [newSenderState, newReceiverState, assetState, newSignerStates, newFactoryState, newAssetStates] =
181
+ await Promise.all([
182
+ // update sender
183
+ statedb.account.updateOrCreate(
184
+ senderState,
185
+ account.updateOrCreate(
186
+ senderState,
187
+ Object.assign({ address: tx.from, nonce: tx.nonce, pk: tx.pk }, signerUpdates[tx.from] || {}),
188
+ context
189
+ ),
190
+ context
191
+ ),
192
+
193
+ // create receiver if not exist: asset owner
194
+ ownerState
195
+ ? Promise.resolve(ownerState)
196
+ : statedb.account.create(owner, account.create({ address: owner }, context), context),
197
+
198
+ // Create asset
199
+ statedb.asset.create(
200
+ itx.address,
201
+ asset.create({ ...mintedAsset, owner, address: mintedAddress }, context),
202
+ context
203
+ ),
204
+
205
+ // Update signer state
206
+ Promise.all(
207
+ signerStates
208
+ .filter((x) => x.address !== tx.from)
209
+ .map((x) =>
210
+ statedb.account.update(x.address, account.update(x, signerUpdates[x.address], context), context)
211
+ )
212
+ ),
213
+
214
+ // Update factory state
215
+ statedb.factory.update(factoryState.address, factory.update(factoryState, factoryUpdates, context), context),
216
+
217
+ // Mark asset as consumed
218
+ Promise.all(
219
+ assetStates.map((x) =>
220
+ statedb.asset.update(x.address, asset.update(x, { consumedTime: txTime }, context), context)
221
+ )
222
+ ),
223
+ ]);
224
+
225
+ context.senderState = newSenderState;
226
+ context.receiverState = newReceiverState;
227
+ context.signerStates = isAlsoSigner ? newSignerStates.concat(newSenderState) : newSignerStates;
228
+ context.assetState = assetState;
229
+ context.factoryState = newFactoryState;
230
+ context.assetStates = newAssetStates;
231
+
232
+ debug('acquire-v3', assetState);
233
+
234
+ next();
235
+ },
236
+ { persistError: true }
237
+ );
238
+
239
+ // run minting hooks for instant settlement
240
+ runner.use(execMintHook, { persistError: true });
241
+
242
+ module.exports = runner;
@@ -0,0 +1,5 @@
1
+ # ABOUT
2
+
3
+ This folder contains reusable pipelines for contract calls.
4
+
5
+ These pipelines does not actually update the statedb, it just return an `updateSet` for the caller to execute.
@@ -0,0 +1,37 @@
1
+ const cloneDeep = require('lodash/cloneDeep');
2
+ const { BN } = require('@ocap/util');
3
+ const { Runner, pipes } = require('@ocap/tx-pipeline');
4
+
5
+ // eslint-disable-next-line global-require
6
+ const debug = require('debug')(`${require('../../../../package.json').name}:acquire-asset:transfer-token`);
7
+
8
+ const runner = new Runner();
9
+
10
+ runner.use(pipes.ExtractState({ from: 'args.to', to: 'receiverState', status: 'INVALID_RECEIVER_STATE' }));
11
+
12
+ runner.use(async (context, next) => {
13
+ const { states, receiverState, args } = context;
14
+ const { tokenAddress, amount } = args;
15
+
16
+ const delta = new BN(amount);
17
+ const newTokens = cloneDeep(receiverState.tokens || {});
18
+ if (newTokens[tokenAddress]) {
19
+ const old = new BN(newTokens[tokenAddress]);
20
+ newTokens[tokenAddress] = old.add(delta).toString();
21
+ } else {
22
+ newTokens[tokenAddress] = delta.toString();
23
+ }
24
+
25
+ context.updateSet = {
26
+ address: receiverState.address,
27
+ updates: states.account.update(receiverState, { tokens: newTokens }, context),
28
+ delta: amount,
29
+ token: tokenAddress,
30
+ };
31
+
32
+ debug('contract.transfer-token', context.updateSet);
33
+
34
+ next();
35
+ });
36
+
37
+ module.exports = runner;
@@ -0,0 +1,29 @@
1
+ const { BN } = require('@ocap/util');
2
+ const { Runner, pipes } = require('@ocap/tx-pipeline');
3
+
4
+ // eslint-disable-next-line global-require
5
+ const debug = require('debug')(`${require('../../../../package.json').name}:acquire-asset:transfer`);
6
+
7
+ const runner = new Runner();
8
+
9
+ runner.use(pipes.ExtractState({ from: 'args.to', to: 'receiverState', status: 'INVALID_RECEIVER_STATE' }));
10
+
11
+ runner.use(async (context, next) => {
12
+ const { states, receiverState, args } = context;
13
+
14
+ const old = new BN(receiverState.balance);
15
+ const delta = new BN(args.amount);
16
+
17
+ context.updateSet = {
18
+ address: receiverState.address,
19
+ updates: states.account.update(receiverState, { balance: old.add(delta).toString() }, context),
20
+ delta: args.amount,
21
+ token: '',
22
+ };
23
+
24
+ debug('contract.transfer', context.updateSet);
25
+
26
+ next();
27
+ });
28
+
29
+ module.exports = runner;