@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,44 +1,49 @@
1
+ /* eslint-disable indent */
2
+ const Error = require('@ocap/util/lib/error');
3
+ const isEmpty = require('lodash/isEmpty');
1
4
  const cloneDeep = require('lodash/cloneDeep');
2
- const isEmpty = require('empty-value');
3
- const { toAssetAddress } = require('@arcblock/did-util');
4
5
  const { Runner, pipes } = require('@ocap/tx-pipeline');
5
6
  const { account, asset } = require('@ocap/state');
7
+ const { toAssetAddress } = require('@arcblock/did-util');
6
8
 
7
9
  // eslint-disable-next-line global-require
8
10
  const debug = require('debug')(`${require('../../../package.json').name}:create-asset`);
9
11
 
10
- const { decodeItxData } = require('../../util');
12
+ const { decodeAnySafe } = require('../../util');
13
+ const ensureServiceFee = require('../rollup/pipes/ensure-service-fee');
11
14
 
12
15
  const runner = new Runner();
13
16
 
14
- runner.use(pipes.VerifyMultiSig(0));
15
-
17
+ // Verify itx
18
+ runner.use(({ itx }, next) => {
19
+ const { error } = asset.schema.validate(itx);
20
+ if (error) {
21
+ return next(new Error('INVALID_TX', `Invalid itx: ${error.message}`));
22
+ }
23
+ return next();
24
+ });
16
25
  runner.use(
17
26
  pipes.VerifyInfo([
18
- {
19
- error: 'INSUFFICIENT_DATA',
20
- message: 'itx.data or itx.address must not be empty',
21
- fn: ({ itx }) => itx.address && itx.data,
22
- },
23
27
  {
24
28
  error: 'INVALID_ASSET',
25
29
  message: 'Asset address is not valid',
26
30
  fn: (context) => {
27
31
  const itx = cloneDeep(context.itx);
28
- itx.data = decodeItxData(itx.data);
29
- delete itx.address;
32
+ itx.data = decodeAnySafe(itx.data);
33
+ itx.address = '';
34
+
35
+ // Save for later use
36
+ context.assetData = itx.data;
37
+ context.signers = [itx.issuer].filter(Boolean);
38
+
30
39
  return toAssetAddress(itx) === context.itx.address;
31
40
  },
32
41
  },
33
42
  ])
34
43
  );
35
44
 
36
- // Ensure sender exist
37
- runner.use(pipes.ExtractState({ from: 'tx.from', to: 'senderState', status: 'INVALID_SENDER_STATE' }));
38
- runner.use(pipes.VerifyAccountMigration({ senderKey: 'senderState' }));
39
-
40
- // Ensure parent exist
41
- runner.use(pipes.ExtractState({ from: 'itx.parent', to: 'parentAsset', status: 'INVALID_ASSET' }));
45
+ // Ensure issuer signed
46
+ runner.use(pipes.VerifyMultiSigV2({ signersKey: 'signers' }));
42
47
 
43
48
  // Ensure asset not exist
44
49
  runner.use(pipes.ExtractState({ from: 'itx.address', to: 'assetState', status: 'OK' }));
@@ -47,33 +52,77 @@ runner.use(
47
52
  {
48
53
  error: 'INVALID_ASSET',
49
54
  message: 'This asset already exist on chain',
50
- fn: (context) => isEmpty(context.assetState),
55
+ fn: (context) => !context.assetState,
51
56
  },
52
57
  ])
53
58
  );
54
59
 
60
+ // Ensure issuer exist
61
+ runner.use(pipes.ExtractState({ from: 'itx.issuer', to: 'issuerState', status: 'INVALID_ISSUER_STATE', table: 'account' })); // prettier-ignore
62
+ runner.use(pipes.VerifyAccountMigration({ signerKey: 'issuerState' }));
63
+
64
+ // Ensure sender exist
65
+ runner.use(pipes.ExtractState({ from: 'tx.from', to: 'senderState', status: 'INVALID_SENDER_STATE', table: 'account' })); // prettier-ignore
66
+ runner.use(pipes.VerifyAccountMigration({ senderKey: 'senderState' }));
67
+
68
+ // Ensure delegation
69
+ runner.use(pipes.ExtractState({ from: 'tx.delegator', to: 'delegatorState', status: 'OK', table: 'account' }));
70
+ runner.use(pipes.VerifyDelegation({ type: 'signature', signerKey: 'senderState', delegatorKey: 'delegatorState' }));
71
+
72
+ // Check if parent is a factory
73
+ runner.use(pipes.ExtractState({ from: 'itx.parent', to: 'factoryState', status: 'OK', table: 'factory' }));
74
+ runner.use(
75
+ pipes.VerifyInfo([
76
+ // For security consideration, user can not create fake assets from a factory
77
+ // https://github.com/ArcBlock/asset-chain/issues/97
78
+ {
79
+ error: 'CREATE_FROM_FACTORY',
80
+ message: 'You can only get asset from factory from acquire or mint',
81
+ fn: ({ itx, factoryState }) => !(itx.parent && factoryState),
82
+ persist: true,
83
+ },
84
+ ])
85
+ );
86
+
87
+ // Ensure parent exist
88
+ runner.use(pipes.ExtractState({ from: 'itx.parent', to: 'parentAsset', status: 'INVALID_ASSET', table: 'asset' }));
89
+
90
+ runner.use(ensureServiceFee);
91
+
55
92
  // Update asset state
56
- runner.use(async (context, next) => {
57
- const { itx, statedb, senderState } = context;
58
- const data = decodeItxData(itx.data);
93
+ runner.use(
94
+ async (context, next) => {
95
+ const { tx, itx, assetData, statedb, senderState, delegatorState, senderUpdates, vaultState, vaultUpdates } =
96
+ context;
97
+ const owner = delegatorState ? delegatorState.address : senderState.address;
59
98
 
60
- // Update owner state
61
- const ownerState = await statedb.account.update(
62
- senderState.address,
63
- account.update(senderState, { numAssets: senderState.numAssets + 1 }, context),
64
- context
65
- );
99
+ const [newSenderState, assetState, newVaultState] = await Promise.all([
100
+ statedb.account.update(
101
+ senderState.address,
102
+ account.update(senderState, { nonce: tx.nonce, pk: tx.pk, ...senderUpdates }, context),
103
+ context
104
+ ),
66
105
 
67
- // Create asset state
68
- const attrs = asset.create({ ...itx, data, owner: ownerState.address, issuer: ownerState.address }, context);
69
- const assetState = await statedb.asset.create(itx.address, attrs, context);
106
+ statedb.asset.create(
107
+ itx.address,
108
+ asset.create({ ...itx, tags: itx.tagsList, data: assetData, owner }, context),
109
+ context
110
+ ),
70
111
 
71
- context.senderState = ownerState;
72
- context.assetState = assetState;
112
+ isEmpty(vaultUpdates)
113
+ ? vaultState
114
+ : statedb.account.update(vaultState.address, account.update(vaultState, vaultUpdates, context), context),
115
+ ]);
73
116
 
74
- debug('create', assetState);
117
+ context.senderState = newSenderState;
118
+ context.assetState = assetState;
119
+ context.vaultState = newVaultState;
75
120
 
76
- next();
77
- });
121
+ debug('createAsset', assetState);
122
+
123
+ next();
124
+ },
125
+ { persistError: true }
126
+ );
78
127
 
79
128
  module.exports = runner;
@@ -0,0 +1,133 @@
1
+ /* eslint-disable function-paren-newline */
2
+ const { Runner, pipes } = require('@ocap/tx-pipeline');
3
+ const { account, asset, factory } = require('@ocap/state');
4
+
5
+ // eslint-disable-next-line global-require
6
+ const debug = require('debug')(`${require('../../../package.json').name}:acquire-asset`);
7
+
8
+ const verifyAcquireParams = require('./pipes/verify-acquire-params');
9
+ const verifyMintLimit = require('./pipes/verify-mint-limit');
10
+ const verifyItxVariables = require('./pipes/verify-itx-variables');
11
+ const verifyItxAssets = require('./pipes/verify-itx-assets');
12
+ const verifyItxAddress = require('./pipes/verify-itx-address');
13
+
14
+ const runner = new Runner();
15
+
16
+ runner.use(pipes.VerifyMultiSig(0));
17
+ runner.use(verifyAcquireParams('mint'));
18
+
19
+ // ensure asset not exist
20
+ runner.use(pipes.ExtractState({ from: 'itx.address', to: 'duplicateAsset', status: 'OK', table: 'asset' }));
21
+ runner.use(
22
+ pipes.VerifyInfo([
23
+ {
24
+ error: 'FORBIDDEN',
25
+ message: 'Asset with same address already exists',
26
+ fn: ({ duplicateAsset }) => !duplicateAsset,
27
+ persist: true,
28
+ },
29
+ ])
30
+ );
31
+
32
+ // Ensure sender exist
33
+ runner.use(pipes.ExtractState({ from: 'tx.from', to: 'senderState', status: 'INVALID_SENDER_STATE', table: 'account' })); // prettier-ignore
34
+ runner.use(pipes.VerifyAccountMigration({ senderKey: 'senderState' }));
35
+
36
+ // Ensure factory exist
37
+ runner.use(pipes.ExtractState({ from: 'itx.factory', to: 'factoryState', status: 'INVALID_FACTORY_STATE', table: 'factory' })); // prettier-ignore
38
+
39
+ // Used to assemble issuer object
40
+ runner.use(pipes.ExtractState({ from: 'factoryState.owner', to: 'factoryOwnerState', status: 'INVALID_OWNER', table: 'account' })); // prettier-ignore
41
+
42
+ // Ensure factory owner exist and equal to sender
43
+ runner.use(pipes.ExtractState({ from: 'itx.owner', to: 'ownerState', status: 'OK', table: 'account' }));
44
+ runner.use(
45
+ pipes.VerifyInfo([
46
+ {
47
+ error: 'OWNER_ONLY_OPERATION',
48
+ message: 'Only factory owner is allowed to mint',
49
+ fn: ({ factoryState, senderState }) => factoryState.owner === senderState.address,
50
+ persist: true,
51
+ },
52
+ ])
53
+ );
54
+
55
+ // Prepare for itx.assets verify
56
+ runner.use(pipes.ExtractState({ from: 'itx.assetsList', to: 'assetStates', status: 'OK', table: 'asset' }));
57
+ runner.use(
58
+ pipes.ExtractState({
59
+ from: 'factoryState.input.assets',
60
+ to: 'expectedFactoryStates',
61
+ status: 'OK',
62
+ table: 'factory',
63
+ })
64
+ );
65
+
66
+ runner.use(verifyMintLimit);
67
+ runner.use(verifyItxVariables);
68
+ runner.use(verifyItxAddress('mint'));
69
+ runner.use(pipes.VerifyUpdater({ assetKey: 'assetStates', ownerKey: 'ownerState' }));
70
+ runner.use(verifyItxAssets);
71
+
72
+ // update statedb
73
+ runner.use(
74
+ async (context, next) => {
75
+ const {
76
+ tx,
77
+ itx,
78
+ txTime,
79
+ statedb,
80
+ senderState,
81
+ factoryState,
82
+ mintedAsset,
83
+ mintedAddress,
84
+ ownerState,
85
+ assetStates = [],
86
+ } = context;
87
+
88
+ const owner = ownerState ? ownerState.address : itx.owner;
89
+
90
+ const senderUpdates = { nonce: tx.nonce, pk: tx.pk };
91
+ const factoryUpdates = { numMinted: factoryState.numMinted + 1 };
92
+
93
+ const [newSenderState, newReceiverState, assetState, newFactoryState, newAssetStates] = await Promise.all([
94
+ // update sender
95
+ statedb.account.update(senderState.address, account.update(senderState, senderUpdates, context), context),
96
+
97
+ // create receiver if not exist: asset owner
98
+ ownerState
99
+ ? Promise.resolve(ownerState)
100
+ : statedb.account.create(owner, account.create({ address: owner }, context), context),
101
+
102
+ // create asset
103
+ statedb.asset.create(
104
+ itx.address,
105
+ asset.create({ ...mintedAsset, owner, address: mintedAddress }, context),
106
+ context
107
+ ),
108
+
109
+ // update factory
110
+ statedb.factory.update(factoryState.address, factory.update(factoryState, factoryUpdates, context), context),
111
+
112
+ // mark input assets as consumed
113
+ Promise.all(
114
+ assetStates.map((x) =>
115
+ statedb.asset.update(x.address, asset.update(x, { consumedTime: txTime }, context), context)
116
+ )
117
+ ),
118
+ ]);
119
+
120
+ context.senderState = newSenderState;
121
+ context.receiverState = newReceiverState;
122
+ context.assetState = assetState;
123
+ context.factoryState = newFactoryState;
124
+ context.assetStates = newAssetStates;
125
+
126
+ debug('mint', assetState);
127
+
128
+ next();
129
+ },
130
+ { persistError: true }
131
+ );
132
+
133
+ module.exports = runner;
@@ -0,0 +1,59 @@
1
+ /* eslint-disable no-await-in-loop */
2
+ // contract calls
3
+ const cloneDeep = require('lodash/cloneDeep');
4
+ const transferCall = require('../calls/transfer');
5
+ const transferTokenCall = require('../calls/transfer-token');
6
+
7
+ const contractCalls = {
8
+ transfer: transferCall,
9
+ transferToken: transferTokenCall,
10
+ };
11
+
12
+ module.exports = async (context, next) => {
13
+ const { statedb, factoryState, tokenUpdates, config } = context;
14
+
15
+ if (factoryState.settlement === 'instant') {
16
+ const mintHook = factoryState.hooks.find((x) => x.name === 'mint');
17
+ if (mintHook && mintHook.type === 'contract') {
18
+ context.updatedAccounts = [];
19
+
20
+ // These calls must be run sequentially, because different calls may update same account
21
+ const clone = cloneDeep(mintHook.compiled);
22
+ for (const x of clone) {
23
+ // Convert transfer to transferToken call for backwards compatibility
24
+ if (x.call === 'transfer') {
25
+ x.call = 'transferToken';
26
+ x.args.tokenAddress = config.token.address;
27
+ }
28
+
29
+ const pipeline = contractCalls[x.call];
30
+ const { updateSet } = await pipeline.runAsync({
31
+ states: context.states,
32
+ statedb,
33
+ txn: context.txn,
34
+ txTime: context.txTime,
35
+ txHash: context.txHash,
36
+ args: x.args,
37
+ });
38
+
39
+ await statedb.account.update(updateSet.address, updateSet.updates, context);
40
+ context.updatedAccounts.push(updateSet);
41
+ }
42
+ }
43
+ } else {
44
+ context.receipts = [
45
+ {
46
+ address: factoryState.address,
47
+ changes: Object.keys(tokenUpdates.tokens)
48
+ .map((x) => ({
49
+ target: x,
50
+ action: 'transfer',
51
+ value: tokenUpdates.tokens[x],
52
+ }))
53
+ .filter((x) => x.value > 0),
54
+ },
55
+ ];
56
+ }
57
+
58
+ next();
59
+ };
@@ -0,0 +1,18 @@
1
+ const uniqBy = require('lodash/uniqBy');
2
+ const { BN } = require('@ocap/util');
3
+ const createSortedList = require('@ocap/util/lib/create-sorted-list');
4
+
5
+ // Create token requirements from factoryState
6
+ module.exports = (context, next) => {
7
+ const { factoryState, config } = context;
8
+ context.factoryTokens = [...factoryState.input.tokens];
9
+
10
+ const exist = context.factoryTokens.find((x) => x.address === config.token.address);
11
+ if (!exist && new BN(factoryState.input.value).gt(new BN(0))) {
12
+ context.factoryTokens.push({ address: config.token.address, value: factoryState.input.value });
13
+ }
14
+
15
+ context.factoryTokens = uniqBy(context.factoryTokens, 'address');
16
+ context.tokenAddress = createSortedList(context.factoryTokens.map((x) => x.address));
17
+ next();
18
+ };
@@ -0,0 +1,30 @@
1
+ const Error = require('@ocap/util/lib/error');
2
+ const getListField = require('@ocap/util/lib/get-list-field');
3
+
4
+ module.exports =
5
+ (mode) =>
6
+ ({ itx }, next) => {
7
+ if (!itx.factory) {
8
+ return next(new Error('INSUFFICIENT_DATA', 'itx.factory must not be empty'));
9
+ }
10
+ if (!itx.address) {
11
+ return next(new Error('INSUFFICIENT_DATA', 'itx.address must not be empty'));
12
+ }
13
+ if (mode === 'acquire-v2') {
14
+ if (!itx.issuer) {
15
+ return next(new Error('INSUFFICIENT_DATA', 'itx.issuer must not be empty'));
16
+ }
17
+ }
18
+ if (['mint', 'acquire-v3'].includes(mode)) {
19
+ if (!itx.owner) {
20
+ return next(new Error('INSUFFICIENT_DATA', 'itx.owner must not be empty'));
21
+ }
22
+ }
23
+
24
+ const variables = getListField(itx, 'variables');
25
+ if (!Array.isArray(variables) && !variables.length) {
26
+ return next(new Error('INSUFFICIENT_DATA', 'itx.variables must not be empty'));
27
+ }
28
+
29
+ return next();
30
+ };
@@ -0,0 +1,41 @@
1
+ const Error = require('@ocap/util/lib/error');
2
+ const { mintFromFactory } = require('@ocap/asset');
3
+
4
+ module.exports = (mode) => (context, next) => {
5
+ const { itx, senderState, ownerState, factoryState, delegatorState, factoryOwnerState, factoryInputs } = context;
6
+
7
+ let issuer = null;
8
+ let owner = null;
9
+
10
+ if (mode === 'acquire-v2') {
11
+ owner = delegatorState || senderState;
12
+ issuer = itx.issuer;
13
+ context.assetOwner = owner;
14
+ } else if (mode === 'acquire-v3') {
15
+ owner = ownerState || { address: itx.owner };
16
+ issuer = itx.issuer;
17
+ } else if (mode === 'mint') {
18
+ owner = ownerState || { address: itx.owner };
19
+ issuer = {
20
+ id: factoryOwnerState.address,
21
+ pk: factoryOwnerState.pk,
22
+ name: factoryOwnerState.moniker,
23
+ };
24
+ }
25
+
26
+ const minted = mintFromFactory({
27
+ factory: factoryState,
28
+ inputs: factoryInputs,
29
+ owner: owner.address,
30
+ issuer,
31
+ });
32
+
33
+ if (minted.address !== itx.address) {
34
+ return next(new Error('INVALID_ASSET', 'Invalid itx.address: does not match with minted asset address'));
35
+ }
36
+
37
+ context.mintedAsset = minted.asset;
38
+ context.mintedAddress = minted.address;
39
+
40
+ return next();
41
+ };
@@ -0,0 +1,49 @@
1
+ const Error = require('@ocap/util/lib/error');
2
+ const getListField = require('@ocap/util/lib/get-list-field');
3
+
4
+ module.exports = (context, next) => {
5
+ const { factoryState, expectedFactoryStates = [], assetStates: actualAssetStates = [], itx } = context;
6
+ const { assets: expectedAssets } = factoryState.input;
7
+
8
+ if (expectedAssets.length === 0) {
9
+ return next();
10
+ }
11
+
12
+ const inputAssets = getListField(itx, 'assets');
13
+
14
+ // Are all input assets found on chain?
15
+ if (inputAssets.length > actualAssetStates.length) {
16
+ return next(new Error('INVALID_ASSET', 'Input asset does not exist on chain'));
17
+ }
18
+
19
+ // https://github.com/ArcBlock/asset-chain/issues/96
20
+ // If factory.input.assets are all factory
21
+ if (expectedFactoryStates.length === expectedAssets.length) {
22
+ // Just ensure that input assets are minted from any of the factory
23
+ // They do not need to match on number
24
+ if (actualAssetStates.every((x) => x.parent && expectedAssets.includes(x.parent) && !x.consumedTime)) {
25
+ return next();
26
+ }
27
+
28
+ return next(new Error('INVALID_ASSET', 'Input asset not minted from any of factory.input.assets'));
29
+ }
30
+
31
+ // For specific assets, they must not be consumed
32
+ // For factory, they must exist on chain
33
+ // eslint-disable-next-line no-restricted-syntax
34
+ for (const address of expectedAssets) {
35
+ const plainAsset = actualAssetStates.find((x) => x.address === address);
36
+ if (plainAsset) {
37
+ if (plainAsset.consumedTime) {
38
+ return next(new Error('INVALID_ASSET', `Input asset ${address} already consumed`));
39
+ }
40
+ } else {
41
+ const childAsset = actualAssetStates.find((x) => x.parent === address);
42
+ if (!childAsset) {
43
+ return next(new Error('INVALID_ASSET', `Input asset not found ${address}`));
44
+ }
45
+ }
46
+ }
47
+
48
+ return next();
49
+ };
@@ -0,0 +1,26 @@
1
+ const Error = require('@ocap/util/lib/error');
2
+ const getListField = require('@ocap/util/lib/get-list-field');
3
+
4
+ module.exports = (context, next) => {
5
+ const { factoryState, itx } = context;
6
+ const { variables } = factoryState.input;
7
+
8
+ // validate input variables
9
+ const factoryInputs = getListField(itx, 'variables').reduce((acc, x) => {
10
+ acc[x.name] = x.value;
11
+ return acc;
12
+ }, {});
13
+ const missingVariables = variables.filter((x) => x.required && !factoryInputs[x.name]);
14
+ if (missingVariables.length) {
15
+ return next(
16
+ new Error(
17
+ 'INVALID_FACTORY_INPUT',
18
+ `Invalid itx.variables: missing required input variable: ${missingVariables.map((x) => x.name).join(', ')}`
19
+ )
20
+ );
21
+ }
22
+
23
+ context.factoryInputs = factoryInputs;
24
+
25
+ return next();
26
+ };
@@ -0,0 +1,13 @@
1
+ const Error = require('@ocap/util/lib/error');
2
+
3
+ module.exports = ({ factoryState }, next) => {
4
+ if (factoryState.limit === 0) {
5
+ return next();
6
+ }
7
+
8
+ if (factoryState.numMinted < factoryState.limit) {
9
+ return next();
10
+ }
11
+
12
+ return next(new Error('EXCEED_MINT_LIMIT', 'This request will exceed factory mint limit'));
13
+ };