@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.
- package/README.md +36 -0
- package/lib/execute.js +97 -30
- package/lib/index.js +56 -14
- package/lib/protocols/account/declare.js +36 -30
- package/lib/protocols/account/delegate.js +78 -40
- package/lib/protocols/account/migrate.js +65 -49
- package/lib/protocols/account/revoke-delegate.js +39 -23
- package/lib/protocols/asset/acquire-v2.js +159 -0
- package/lib/protocols/asset/acquire-v3.js +242 -0
- package/lib/protocols/asset/calls/README.md +5 -0
- package/lib/protocols/asset/calls/transfer-token.js +37 -0
- package/lib/protocols/asset/calls/transfer.js +29 -0
- package/lib/protocols/asset/create.js +85 -36
- package/lib/protocols/asset/mint.js +133 -0
- package/lib/protocols/asset/pipes/exec-mint-hook.js +59 -0
- package/lib/protocols/asset/pipes/extract-factory-tokens.js +18 -0
- package/lib/protocols/asset/pipes/verify-acquire-params.js +30 -0
- package/lib/protocols/asset/pipes/verify-itx-address.js +41 -0
- package/lib/protocols/asset/pipes/verify-itx-assets.js +49 -0
- package/lib/protocols/asset/pipes/verify-itx-variables.js +26 -0
- package/lib/protocols/asset/pipes/verify-mint-limit.js +13 -0
- package/lib/protocols/asset/update.js +76 -44
- package/lib/protocols/factory/create.js +146 -0
- package/lib/protocols/governance/claim-stake.js +219 -0
- package/lib/protocols/governance/revoke-stake.js +136 -0
- package/lib/protocols/governance/stake.js +176 -0
- package/lib/protocols/rollup/claim-reward.js +283 -0
- package/lib/protocols/rollup/create-block.js +333 -0
- package/lib/protocols/rollup/create.js +169 -0
- package/lib/protocols/rollup/join.js +156 -0
- package/lib/protocols/rollup/leave.js +127 -0
- package/lib/protocols/rollup/migrate-contract.js +53 -0
- package/lib/protocols/rollup/migrate-token.js +66 -0
- package/lib/protocols/rollup/pause.js +52 -0
- package/lib/protocols/rollup/pipes/ensure-service-fee.js +37 -0
- package/lib/protocols/rollup/pipes/ensure-validator.js +10 -0
- package/lib/protocols/rollup/pipes/verify-evidence.js +37 -0
- package/lib/protocols/rollup/pipes/verify-paused.js +10 -0
- package/lib/protocols/rollup/pipes/verify-signers.js +88 -0
- package/lib/protocols/rollup/resume.js +52 -0
- package/lib/protocols/rollup/update.js +98 -0
- package/lib/protocols/token/create.js +150 -0
- package/lib/protocols/token/deposit-v2.js +241 -0
- package/lib/protocols/token/withdraw-v2.js +255 -0
- package/lib/protocols/trade/exchange-v2.js +179 -0
- package/lib/protocols/trade/transfer-v2.js +136 -0
- package/lib/protocols/trade/transfer-v3.js +241 -0
- package/lib/util.js +325 -2
- package/package.json +23 -16
- package/lib/protocols/misc/poke.js +0 -106
- package/lib/protocols/trade/exchange.js +0 -139
- package/lib/protocols/trade/transfer.js +0 -101
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const get = require('lodash/get');
|
|
2
|
-
const
|
|
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: '
|
|
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
|
-
//
|
|
54
|
-
runner.use(
|
|
55
|
-
|
|
61
|
+
// Update delegation state
|
|
62
|
+
runner.use(
|
|
63
|
+
async (context, next) => {
|
|
64
|
+
const { tx, itx, typeUrls, statedb, senderState, delegationState } = context;
|
|
56
65
|
|
|
57
|
-
|
|
66
|
+
debug('update', { address: itx.address, typeUrls });
|
|
58
67
|
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
64
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
80
|
+
// Update context
|
|
81
|
+
context.senderState = newSenderState;
|
|
82
|
+
context.delegationState = newDelegationState;
|
|
69
83
|
|
|
70
|
-
|
|
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,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;
|