@ocap/tx-protocols 1.27.7 → 1.27.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/protocols/governance/stake.js +9 -0
- package/lib/protocols/token/create.js +13 -2
- package/lib/protocols/token-factory/burn.js +64 -45
- package/lib/protocols/token-factory/create.js +28 -5
- package/lib/protocols/token-factory/mint.js +91 -63
- package/lib/protocols/token-factory/pipes/calc-reserve.js +2 -2
- package/lib/protocols/trade/exchange-v2.js +8 -0
- package/lib/protocols/trade/transfer-v2.js +8 -0
- package/lib/protocols/trade/transfer-v3.js +8 -0
- package/package.json +17 -17
|
@@ -88,6 +88,15 @@ runner.use(pipes.VerifyAccountMigration({ stateKey: 'receiverState', addressKey:
|
|
|
88
88
|
runner.use(pipes.ExtractState({ from: 'tokens', to: 'tokenStates', status: 'INVALID_TOKEN', table: 'token' }));
|
|
89
89
|
runner.use(pipes.VerifyTokenBalance({ ownerKey: 'signerStates', conditionKey: 'inputs' }));
|
|
90
90
|
|
|
91
|
+
// verify token spenders
|
|
92
|
+
runner.use(
|
|
93
|
+
pipes.verifyTokenAccess({
|
|
94
|
+
statesKey: 'tokenStates',
|
|
95
|
+
listFieldKey: 'spenders',
|
|
96
|
+
accountKeys: ['signerStates'],
|
|
97
|
+
errorMessage: 'Account {address} is not allowed to stake token {tokenAddress}',
|
|
98
|
+
})
|
|
99
|
+
);
|
|
91
100
|
// 7. verify asset state and ownership
|
|
92
101
|
runner.use(pipes.ExtractState({ from: 'assets', to: 'assetStates', status: 'OK', table: 'asset' }));
|
|
93
102
|
runner.use(pipes.VerifyTransferrable({ assets: 'assetStates' }));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const isEmpty = require('empty-value');
|
|
2
2
|
const cloneDeep = require('lodash/cloneDeep');
|
|
3
|
+
const uniq = require('lodash/uniq');
|
|
3
4
|
const { Joi, schemas } = require('@arcblock/validator');
|
|
4
5
|
const { CustomError: Error } = require('@ocap/util/lib/error');
|
|
5
6
|
const { Runner, pipes } = require('@ocap/tx-pipeline');
|
|
@@ -35,16 +36,22 @@ const schema = Joi.object({
|
|
|
35
36
|
.optional()
|
|
36
37
|
.allow(null),
|
|
37
38
|
foreignToken: schemas.foreignToken.optional().allow(null).default(null),
|
|
39
|
+
spendersList: Joi.array().items(Joi.DID().prefix()).max(30).optional().allow(null).default(null),
|
|
38
40
|
tokenFactoryAddress: Joi.forbidden(),
|
|
39
41
|
data: Joi.any().optional().allow(null),
|
|
40
42
|
}).options({ stripUnknown: true, noDefaults: false });
|
|
41
43
|
|
|
42
44
|
// 1. verify itx
|
|
43
45
|
runner.use(({ itx }, next) => {
|
|
44
|
-
const { error } = schema.validate(itx);
|
|
46
|
+
const { error, value } = schema.validate(itx);
|
|
45
47
|
if (error) {
|
|
46
48
|
return next(new Error('INVALID_TX', `Invalid itx: ${error.message}`));
|
|
47
49
|
}
|
|
50
|
+
|
|
51
|
+
// convert spendersList to spenders
|
|
52
|
+
itx.spenders = value.spendersList?.length ? uniq(value.spendersList) : null;
|
|
53
|
+
delete itx.spendersList;
|
|
54
|
+
|
|
48
55
|
return next();
|
|
49
56
|
});
|
|
50
57
|
|
|
@@ -152,7 +159,11 @@ runner.use(
|
|
|
152
159
|
context
|
|
153
160
|
),
|
|
154
161
|
|
|
155
|
-
statedb.token.create(
|
|
162
|
+
statedb.token.create(
|
|
163
|
+
itx.address,
|
|
164
|
+
token.create({ ...cloneDeep(itx), data, issuer: owner, type: 'Token' }, context),
|
|
165
|
+
context
|
|
166
|
+
),
|
|
156
167
|
|
|
157
168
|
delegatorState
|
|
158
169
|
? statedb.account.update(
|
|
@@ -29,6 +29,26 @@ runner.use(({ itx }, next) => {
|
|
|
29
29
|
return next();
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
+
// verify token factory
|
|
33
|
+
runner.use(
|
|
34
|
+
pipes.ExtractState({
|
|
35
|
+
from: 'itx.tokenFactory',
|
|
36
|
+
to: 'tokenFactoryState',
|
|
37
|
+
status: 'INVALID_TOKEN_FACTORY',
|
|
38
|
+
table: 'tokenFactory',
|
|
39
|
+
})
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// ensure token state
|
|
43
|
+
runner.use(
|
|
44
|
+
pipes.ExtractState({
|
|
45
|
+
from: 'tokenFactoryState.tokenAddress',
|
|
46
|
+
to: 'tokenState',
|
|
47
|
+
status: 'INVALID_TOKEN',
|
|
48
|
+
table: 'token',
|
|
49
|
+
})
|
|
50
|
+
);
|
|
51
|
+
|
|
32
52
|
// verify inputs
|
|
33
53
|
runner.use(
|
|
34
54
|
pipes.VerifyTxInput({
|
|
@@ -46,16 +66,6 @@ runner.use(pipes.VerifyListSize({ listKey: ['inputs', 'senders', 'tokens', 'asse
|
|
|
46
66
|
// verify multi sig
|
|
47
67
|
runner.use(pipes.VerifyMultiSigV2({ signersKey: 'senders' }));
|
|
48
68
|
|
|
49
|
-
// verify token factory
|
|
50
|
-
runner.use(
|
|
51
|
-
pipes.ExtractState({
|
|
52
|
-
from: 'itx.tokenFactory',
|
|
53
|
-
to: 'tokenFactoryState',
|
|
54
|
-
status: 'INVALID_TOKEN_FACTORY',
|
|
55
|
-
table: 'tokenFactory',
|
|
56
|
-
})
|
|
57
|
-
);
|
|
58
|
-
|
|
59
69
|
// verify inputs token
|
|
60
70
|
runner.use((context, next) => {
|
|
61
71
|
const { tokenFactoryState, inputs } = context;
|
|
@@ -98,13 +108,13 @@ runner.use(
|
|
|
98
108
|
})
|
|
99
109
|
);
|
|
100
110
|
|
|
101
|
-
//
|
|
111
|
+
// verify minters restriction
|
|
102
112
|
runner.use(
|
|
103
|
-
pipes.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
113
|
+
pipes.verifyTokenAccess({
|
|
114
|
+
statesKey: 'tokenState',
|
|
115
|
+
listFieldKey: 'minters',
|
|
116
|
+
accountKeys: ['signerStates', 'senderState'],
|
|
117
|
+
errorMessage: 'Account {address} is not allowed to burn token {tokenAddress}',
|
|
108
118
|
})
|
|
109
119
|
);
|
|
110
120
|
|
|
@@ -135,25 +145,29 @@ runner.use(
|
|
|
135
145
|
feeKey: 'reserveFee',
|
|
136
146
|
amountKey: 'burnAmount',
|
|
137
147
|
direction: 'burn',
|
|
138
|
-
})
|
|
148
|
+
}),
|
|
149
|
+
{ shouldSkip: ({ tokenFactoryState }) => !tokenFactoryState.curve }
|
|
139
150
|
);
|
|
140
151
|
|
|
141
152
|
// verify slippage
|
|
142
|
-
runner.use(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
153
|
+
runner.use(
|
|
154
|
+
(context, next) => {
|
|
155
|
+
const { reserveAmount, reserveFee } = context;
|
|
156
|
+
const { minReserve } = context.itx;
|
|
157
|
+
|
|
158
|
+
if (minReserve && new BN(reserveAmount).lt(new BN(minReserve).add(new BN(reserveFee || '0')))) {
|
|
159
|
+
return next(
|
|
160
|
+
new Error(
|
|
161
|
+
'SLIPPAGE_EXCEEDED',
|
|
162
|
+
`Burn token failed due to price movement. Expected minimum: ${fromUnitToToken(minReserve)}, actual: ${fromUnitToToken(reserveAmount)}. Try reducing your minReserve.`
|
|
163
|
+
)
|
|
164
|
+
);
|
|
165
|
+
}
|
|
154
166
|
|
|
155
|
-
|
|
156
|
-
}
|
|
167
|
+
return next();
|
|
168
|
+
},
|
|
169
|
+
{ shouldSkip: ({ tokenFactoryState }) => !tokenFactoryState.curve }
|
|
170
|
+
);
|
|
157
171
|
|
|
158
172
|
// verify balance
|
|
159
173
|
runner.use(pipes.ExtractState({ from: 'tokens', to: 'tokenStates', status: 'INVALID_TOKEN', table: 'token' }));
|
|
@@ -165,7 +179,7 @@ runner.use((context, next) => {
|
|
|
165
179
|
if (new BN(tokenFactoryState.currentSupply).lt(new BN(context.burnAmount))) {
|
|
166
180
|
return next(new Error('INSUFFICIENT_FUND', 'Token factory supply is not enough'));
|
|
167
181
|
}
|
|
168
|
-
if (new BN(tokenFactoryState.reserveBalance).lt(new BN(context.reserveAmount))) {
|
|
182
|
+
if (new BN(tokenFactoryState.reserveBalance).lt(new BN(context.reserveAmount || '0'))) {
|
|
169
183
|
return next(new Error('INSUFFICIENT_FUND', 'Token factory reserve balance is not enough'));
|
|
170
184
|
}
|
|
171
185
|
return next();
|
|
@@ -182,10 +196,12 @@ runner.use(
|
|
|
182
196
|
result.create += 1;
|
|
183
197
|
}
|
|
184
198
|
|
|
185
|
-
if (context.
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
199
|
+
if (context.tokenFactoryState.curve) {
|
|
200
|
+
if (context.receiverState) {
|
|
201
|
+
result.update += 1;
|
|
202
|
+
} else {
|
|
203
|
+
result.create += 1;
|
|
204
|
+
}
|
|
189
205
|
}
|
|
190
206
|
|
|
191
207
|
if (context.reserveFee) {
|
|
@@ -262,14 +278,17 @@ runner.use(
|
|
|
262
278
|
}
|
|
263
279
|
|
|
264
280
|
// update receiver
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
address: receiver,
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
281
|
+
// should not update receiver for credit token
|
|
282
|
+
if (tokenFactoryState.curve) {
|
|
283
|
+
accountUpdates[receiver] = applyTokenChange(
|
|
284
|
+
accountUpdates[receiver] || receiverState || { address: receiver, tokens: {} },
|
|
285
|
+
{
|
|
286
|
+
address: receiver,
|
|
287
|
+
token: reserveAddress,
|
|
288
|
+
delta: new BN(reserveAmount).sub(new BN(reserveFee || '0')).toString(), // reserveAmount - reserveFee
|
|
289
|
+
}
|
|
290
|
+
);
|
|
291
|
+
}
|
|
273
292
|
|
|
274
293
|
// update sender
|
|
275
294
|
if (senderChange) {
|
|
@@ -307,7 +326,7 @@ runner.use(
|
|
|
307
326
|
tokenFactoryState,
|
|
308
327
|
{
|
|
309
328
|
currentSupply: new BN(tokenFactoryState.currentSupply).sub(new BN(burnAmount)).toString(),
|
|
310
|
-
reserveBalance: new BN(tokenFactoryState.reserveBalance).sub(new BN(reserveAmount)).toString(),
|
|
329
|
+
reserveBalance: new BN(tokenFactoryState.reserveBalance).sub(new BN(reserveAmount || '0')).toString(),
|
|
311
330
|
},
|
|
312
331
|
context
|
|
313
332
|
),
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
const isEmpty = require('empty-value');
|
|
2
2
|
const cloneDeep = require('lodash/cloneDeep');
|
|
3
|
+
const uniq = require('lodash/uniq');
|
|
3
4
|
const { Joi } = require('@arcblock/validator');
|
|
4
5
|
const { CustomError: Error } = require('@ocap/util/lib/error');
|
|
5
6
|
const { Runner, pipes } = require('@ocap/tx-pipeline');
|
|
6
7
|
const { account, token, tokenFactory, delegation, stake } = require('@ocap/state');
|
|
7
8
|
const { toTokenFactoryAddress, toTokenAddress, toStakeAddress } = require('@arcblock/did-util');
|
|
8
|
-
const { BN, fromTokenToUnit } = require('@ocap/util');
|
|
9
|
+
const { BN, fromTokenToUnit, fromUnitToToken } = require('@ocap/util');
|
|
9
10
|
|
|
10
11
|
// eslint-disable-next-line global-require, import/order
|
|
11
12
|
const debug = require('debug')(`${require('../../../package.json').name}:create-token-factory`);
|
|
@@ -26,7 +27,13 @@ runner.use(pipes.VerifyMultiSig(0));
|
|
|
26
27
|
const schema = Joi.object({
|
|
27
28
|
address: Joi.DID().prefix().role('ROLE_TOKEN_FACTORY').required(),
|
|
28
29
|
feeRate: Joi.number().min(0).max(2000).required(),
|
|
29
|
-
curve:
|
|
30
|
+
curve: Joi.when('token.type', {
|
|
31
|
+
is: 'CreditToken',
|
|
32
|
+
then: Joi.valid(null).optional().messages({
|
|
33
|
+
'any.only': 'Should not provide curve for CreditToken',
|
|
34
|
+
}),
|
|
35
|
+
otherwise: tokenFactory.curveSchema.required(),
|
|
36
|
+
}),
|
|
30
37
|
reserveAddress: Joi.DID().prefix().role('ROLE_TOKEN').required(),
|
|
31
38
|
token: Joi.object({
|
|
32
39
|
name: Joi.string().min(1).max(32).required(),
|
|
@@ -45,6 +52,9 @@ const schema = Joi.object({
|
|
|
45
52
|
.optional()
|
|
46
53
|
.allow(null, ''),
|
|
47
54
|
metadata: Joi.any().required(),
|
|
55
|
+
spendersList: Joi.array().items(Joi.DID().prefix()).max(30).optional().allow(null).default(null),
|
|
56
|
+
mintersList: Joi.array().items(Joi.DID().prefix()).max(30).optional().allow(null).default(null),
|
|
57
|
+
type: Joi.string().valid('CreditToken', 'BondingCurveToken').required(),
|
|
48
58
|
}).required(),
|
|
49
59
|
data: Joi.any().optional().allow(null),
|
|
50
60
|
}).options({ stripUnknown: true, noDefaults: false });
|
|
@@ -69,6 +79,14 @@ runner.use((context, next) => {
|
|
|
69
79
|
context.itx.token.metadata = { ...metadata, value: metadataValue };
|
|
70
80
|
}
|
|
71
81
|
|
|
82
|
+
// convert spendersList to spenders
|
|
83
|
+
context.itx.token.spenders = value.token.spendersList?.length ? uniq(value.token.spendersList) : null;
|
|
84
|
+
delete context.itx.token.spendersList;
|
|
85
|
+
|
|
86
|
+
// convert mintersList to minters
|
|
87
|
+
context.itx.token.minters = value.token.mintersList?.length ? uniq(value.token.mintersList) : null;
|
|
88
|
+
delete context.itx.token.mintersList;
|
|
89
|
+
|
|
72
90
|
return next();
|
|
73
91
|
});
|
|
74
92
|
|
|
@@ -162,19 +180,24 @@ runner.use(
|
|
|
162
180
|
pipes.ExtractState({ from: 'stakeAddress', to: 'stakeState', status: 'INVALID_STAKE_STATE', table: 'stake' })
|
|
163
181
|
);
|
|
164
182
|
runner.use((context, next) => {
|
|
165
|
-
const { stakeState, config } = context;
|
|
183
|
+
const { stakeState, config, itx } = context;
|
|
166
184
|
|
|
167
185
|
if (stakeState.revocable === false) {
|
|
168
186
|
return next(new Error('INVALID_STAKE_STATE', `Staking for token creating already locked: ${stakeState.address}`));
|
|
169
187
|
}
|
|
170
188
|
|
|
171
189
|
const actualStake = new BN(stakeState.tokens[config.token.address] || 0);
|
|
172
|
-
const requiredStake = fromTokenToUnit(
|
|
190
|
+
const requiredStake = fromTokenToUnit(
|
|
191
|
+
itx.token.type === 'CreditToken'
|
|
192
|
+
? config.transaction.txStake.createCreditToken
|
|
193
|
+
: config.transaction.txStake.createToken,
|
|
194
|
+
config.token.decimal
|
|
195
|
+
);
|
|
173
196
|
if (actualStake.lt(requiredStake)) {
|
|
174
197
|
return next(
|
|
175
198
|
new Error(
|
|
176
199
|
'INVALID_STAKE_STATE',
|
|
177
|
-
`Insufficient stake amount: required ${config.
|
|
200
|
+
`Insufficient stake amount: required ${fromUnitToToken(requiredStake, config.token.decimal)} ${config.token.symbol}`
|
|
178
201
|
)
|
|
179
202
|
);
|
|
180
203
|
}
|
|
@@ -17,7 +17,7 @@ const schema = Joi.object({
|
|
|
17
17
|
receiver: schemas.tokenHolder.required(),
|
|
18
18
|
amount: Joi.BN().greater(0).required(),
|
|
19
19
|
data: Joi.any().optional().allow(null),
|
|
20
|
-
inputsList: schemas.multiInput.
|
|
20
|
+
inputsList: schemas.multiInput.optional(),
|
|
21
21
|
}).options({ stripUnknown: true, noDefaults: false });
|
|
22
22
|
|
|
23
23
|
// verify itx
|
|
@@ -29,7 +29,26 @@ runner.use(({ itx }, next) => {
|
|
|
29
29
|
return next();
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
-
//
|
|
32
|
+
// ensure token factory state
|
|
33
|
+
runner.use(
|
|
34
|
+
pipes.ExtractState({
|
|
35
|
+
from: 'itx.tokenFactory',
|
|
36
|
+
to: 'tokenFactoryState',
|
|
37
|
+
status: 'INVALID_TOKEN_FACTORY',
|
|
38
|
+
table: 'tokenFactory',
|
|
39
|
+
})
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// ensure token state
|
|
43
|
+
runner.use(
|
|
44
|
+
pipes.ExtractState({
|
|
45
|
+
from: 'tokenFactoryState.tokenAddress',
|
|
46
|
+
to: 'tokenState',
|
|
47
|
+
status: 'INVALID_TOKEN',
|
|
48
|
+
table: 'token',
|
|
49
|
+
})
|
|
50
|
+
);
|
|
51
|
+
|
|
33
52
|
runner.use(
|
|
34
53
|
pipes.VerifyTxInput({
|
|
35
54
|
fieldKey: 'itx.inputs',
|
|
@@ -37,47 +56,48 @@ runner.use(
|
|
|
37
56
|
sendersKey: 'senders',
|
|
38
57
|
tokensKey: 'tokens',
|
|
39
58
|
assetsKey: null,
|
|
40
|
-
})
|
|
59
|
+
}),
|
|
60
|
+
{ shouldSkip: ({ tokenFactoryState }) => !tokenFactoryState.curve }
|
|
41
61
|
);
|
|
42
62
|
|
|
43
63
|
// verify itx size: set hard limit here because more inputs leads to longer tx execute time
|
|
44
|
-
runner.use(pipes.VerifyListSize({ listKey: ['inputs', 'senders', 'tokens', 'assets'] })
|
|
64
|
+
runner.use(pipes.VerifyListSize({ listKey: ['inputs', 'senders', 'tokens', 'assets'] }), {
|
|
65
|
+
shouldSkip: ({ tokenFactoryState }) => !tokenFactoryState.curve,
|
|
66
|
+
});
|
|
45
67
|
|
|
46
68
|
// verify multi sig
|
|
47
|
-
runner.use(pipes.VerifyMultiSigV2({ signersKey: 'senders' })
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
runner.use(
|
|
51
|
-
pipes.ExtractState({
|
|
52
|
-
from: 'itx.tokenFactory',
|
|
53
|
-
to: 'tokenFactoryState',
|
|
54
|
-
status: 'INVALID_TOKEN_FACTORY',
|
|
55
|
-
table: 'tokenFactory',
|
|
56
|
-
})
|
|
57
|
-
);
|
|
69
|
+
runner.use(pipes.VerifyMultiSigV2({ signersKey: 'senders' }), {
|
|
70
|
+
shouldSkip: ({ tokenFactoryState }) => !tokenFactoryState.curve,
|
|
71
|
+
});
|
|
58
72
|
|
|
59
73
|
// verify inputs token
|
|
60
|
-
runner.use(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
(
|
|
65
|
-
input
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
74
|
+
runner.use(
|
|
75
|
+
(context, next) => {
|
|
76
|
+
const { tokenFactoryState, inputs } = context;
|
|
77
|
+
|
|
78
|
+
const isAccepted = inputs.every(
|
|
79
|
+
(input) =>
|
|
80
|
+
input.tokensList.length &&
|
|
81
|
+
input.tokensList.every((x) => x.address === tokenFactoryState.reserveAddress) &&
|
|
82
|
+
!input.assetsList.length
|
|
83
|
+
);
|
|
69
84
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
85
|
+
if (!isAccepted) {
|
|
86
|
+
return next(new Error('INVALID_TX', `Inputs only accept ${tokenFactoryState.reserveAddress}`));
|
|
87
|
+
}
|
|
73
88
|
|
|
74
|
-
|
|
75
|
-
}
|
|
89
|
+
return next();
|
|
90
|
+
},
|
|
91
|
+
{ shouldSkip: ({ tokenFactoryState }) => !tokenFactoryState.curve }
|
|
92
|
+
);
|
|
76
93
|
|
|
77
94
|
// ensure sender
|
|
78
95
|
runner.use(pipes.ExtractState({ from: 'tx.from', to: 'senderState', status: 'OK', table: 'account' }));
|
|
79
|
-
runner.use(pipes.ExtractState({ from: 'senders', to: 'signerStates', status: 'INVALID_SIGNER_STATE', table: 'account' })); // prettier-ignore
|
|
80
|
-
runner.use(
|
|
96
|
+
runner.use(pipes.ExtractState({ from: 'senders', to: 'signerStates', status: 'INVALID_SIGNER_STATE', table: 'account' }), { shouldSkip: ({ tokenFactoryState }) => !tokenFactoryState.curve, }); // prettier-ignore
|
|
97
|
+
runner.use(
|
|
98
|
+
pipes.VerifyAccountMigration({ signerKey: 'signerStates', stateKey: 'senderState', addressKey: 'tx.from' }),
|
|
99
|
+
{ shouldSkip: ({ tokenFactoryState }) => !tokenFactoryState.curve }
|
|
100
|
+
);
|
|
81
101
|
|
|
82
102
|
// ensure receiver
|
|
83
103
|
runner.use(pipes.ExtractState({ from: 'itx.receiver', to: 'receiverState', status: 'OK', table: 'account' }));
|
|
@@ -96,13 +116,13 @@ runner.use(
|
|
|
96
116
|
})
|
|
97
117
|
);
|
|
98
118
|
|
|
99
|
-
//
|
|
119
|
+
// verify minters restriction
|
|
100
120
|
runner.use(
|
|
101
|
-
pipes.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
121
|
+
pipes.verifyTokenAccess({
|
|
122
|
+
statesKey: 'tokenState',
|
|
123
|
+
listFieldKey: 'minters',
|
|
124
|
+
accountKeys: ['senderState', 'receiverState', 'itx.receiver'],
|
|
125
|
+
errorMessage: 'Account {address} is not allowed to mint token {tokenAddress}',
|
|
106
126
|
})
|
|
107
127
|
);
|
|
108
128
|
|
|
@@ -113,7 +133,8 @@ runner.use(
|
|
|
113
133
|
tokenStateKey: 'tokenState',
|
|
114
134
|
reserveKey: 'reserveAmount',
|
|
115
135
|
amountKey: 'itx.amount',
|
|
116
|
-
})
|
|
136
|
+
}),
|
|
137
|
+
{ shouldSkip: ({ tokenFactoryState }) => !tokenFactoryState.curve }
|
|
117
138
|
);
|
|
118
139
|
|
|
119
140
|
// verify max supply
|
|
@@ -137,33 +158,38 @@ runner.use((context, next) => {
|
|
|
137
158
|
});
|
|
138
159
|
|
|
139
160
|
// verify slippage
|
|
140
|
-
runner.use(
|
|
141
|
-
|
|
161
|
+
runner.use(
|
|
162
|
+
(context, next) => {
|
|
163
|
+
const { reserveAmount, reserveFee, inputs, tokenFactoryState } = context;
|
|
164
|
+
|
|
165
|
+
const maxReserve = inputs.reduce((total, input) => {
|
|
166
|
+
const reserveToken = input.tokensList.find((x) => x.address === tokenFactoryState.reserveAddress);
|
|
167
|
+
if (reserveToken) {
|
|
168
|
+
return total.add(new BN(reserveToken.value));
|
|
169
|
+
}
|
|
170
|
+
return total;
|
|
171
|
+
}, new BN(0));
|
|
142
172
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
173
|
+
const actual = new BN(reserveAmount).add(new BN(reserveFee || '0'));
|
|
174
|
+
if (actual.gt(maxReserve)) {
|
|
175
|
+
return next(
|
|
176
|
+
new Error(
|
|
177
|
+
'SLIPPAGE_EXCEEDED',
|
|
178
|
+
`Mint token failed due to price movement. Expected maximum: ${fromUnitToToken(maxReserve)}, actual: ${fromUnitToToken(actual)}. Try increasing your inputs.`
|
|
179
|
+
)
|
|
180
|
+
);
|
|
147
181
|
}
|
|
148
|
-
return total;
|
|
149
|
-
}, new BN(0));
|
|
150
|
-
|
|
151
|
-
const actual = new BN(reserveAmount).add(new BN(reserveFee || '0'));
|
|
152
|
-
if (actual.gt(maxReserve)) {
|
|
153
|
-
return next(
|
|
154
|
-
new Error(
|
|
155
|
-
'SLIPPAGE_EXCEEDED',
|
|
156
|
-
`Mint token failed due to price movement. Expected maximum: ${fromUnitToToken(maxReserve)}, actual: ${fromUnitToToken(actual)}. Try increasing your inputs.`
|
|
157
|
-
)
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
182
|
|
|
161
|
-
|
|
162
|
-
}
|
|
183
|
+
next();
|
|
184
|
+
},
|
|
185
|
+
{ shouldSkip: ({ tokenFactoryState }) => !tokenFactoryState.curve }
|
|
186
|
+
);
|
|
163
187
|
|
|
164
188
|
// verify balance
|
|
165
189
|
runner.use(pipes.ExtractState({ from: 'tokens', to: 'tokenStates', status: 'INVALID_TOKEN', table: 'token' }));
|
|
166
|
-
runner.use(pipes.VerifyTokenBalance({ ownerKey: 'signerStates', conditionKey: 'inputs' })
|
|
190
|
+
runner.use(pipes.VerifyTokenBalance({ ownerKey: 'signerStates', conditionKey: 'inputs' }), {
|
|
191
|
+
shouldSkip: ({ tokenFactoryState }) => !tokenFactoryState.curve,
|
|
192
|
+
});
|
|
167
193
|
|
|
168
194
|
// Ensure tx fee and gas
|
|
169
195
|
runner.use(
|
|
@@ -210,17 +236,19 @@ runner.use(
|
|
|
210
236
|
senderChange,
|
|
211
237
|
updateVaults,
|
|
212
238
|
tokenFactoryState,
|
|
213
|
-
signerStates,
|
|
214
239
|
tokenState,
|
|
215
240
|
reserveAmount,
|
|
216
241
|
reserveFee,
|
|
217
242
|
itx,
|
|
218
|
-
inputs,
|
|
219
243
|
} = context;
|
|
220
244
|
const { amount, receiver } = itx;
|
|
221
245
|
const { reserveAddress, tokenAddress } = tokenFactoryState;
|
|
222
246
|
|
|
223
|
-
|
|
247
|
+
// signerStates and inputs are not exist in context for credit token
|
|
248
|
+
const signerStates = tokenFactoryState.curve ? context.signerStates : [];
|
|
249
|
+
const inputs = tokenFactoryState.curve ? context.inputs : [];
|
|
250
|
+
|
|
251
|
+
const totalReserveCost = new BN(reserveAmount || '0').add(new BN(reserveFee || '0'));
|
|
224
252
|
let currentReserveCost = new BN('0');
|
|
225
253
|
|
|
226
254
|
const accountUpdates = {};
|
|
@@ -311,7 +339,7 @@ runner.use(
|
|
|
311
339
|
tokenFactoryState,
|
|
312
340
|
{
|
|
313
341
|
currentSupply: new BN(tokenFactoryState.currentSupply).add(new BN(amount)).toString(),
|
|
314
|
-
reserveBalance: new BN(tokenFactoryState.reserveBalance).add(new BN(reserveAmount)).toString(),
|
|
342
|
+
reserveBalance: new BN(tokenFactoryState.reserveBalance).add(new BN(reserveAmount || '0')).toString(),
|
|
315
343
|
},
|
|
316
344
|
context
|
|
317
345
|
),
|
|
@@ -28,10 +28,10 @@ module.exports =
|
|
|
28
28
|
const { decimal } = tokenState;
|
|
29
29
|
|
|
30
30
|
const reserveAmount = calcCost({ amount, decimal, currentSupply, direction, curve });
|
|
31
|
-
set(context, reserveKey, reserveAmount
|
|
31
|
+
set(context, reserveKey, reserveAmount?.toString());
|
|
32
32
|
|
|
33
33
|
const reserveFee = calcFee({ reserveAmount, feeRate });
|
|
34
|
-
set(context, feeKey, reserveFee
|
|
34
|
+
set(context, feeKey, reserveFee?.toString());
|
|
35
35
|
|
|
36
36
|
return next();
|
|
37
37
|
};
|
|
@@ -122,6 +122,14 @@ runner.use(pipes.VerifyTokenBalance({ ownerKey: 'receiverState', conditionKey: '
|
|
|
122
122
|
|
|
123
123
|
runner.use(pipes.AntiLandAttack({ senderState: 'senderState', receiverState: 'receiverState' }));
|
|
124
124
|
runner.use(pipes.VerifyBlocked({ stateKeys: ['senderState', 'receiverState'] }));
|
|
125
|
+
runner.use(
|
|
126
|
+
pipes.verifyTokenAccess({
|
|
127
|
+
statesKey: 'tokenStates',
|
|
128
|
+
listFieldKey: 'spenders',
|
|
129
|
+
accountKeys: ['senderState', 'receiverState'],
|
|
130
|
+
errorMessage: 'Account {address} is not allowed to exchange token {tokenAddress}',
|
|
131
|
+
})
|
|
132
|
+
);
|
|
125
133
|
|
|
126
134
|
runner.use(pipes.ExtractState({ from: 'senderAssets', to: 'priv.senderAssets', status: 'INVALID_ASSET', table: 'asset' })); // prettier-ignore
|
|
127
135
|
runner.use(pipes.VerifyTransferrable({ assets: 'priv.senderAssets' }));
|
|
@@ -142,6 +142,14 @@ runner.use(pipes.ExtractReceiver({ from: 'itx.to', to: 'receiver' }));
|
|
|
142
142
|
runner.use(pipes.ExtractState({ from: 'receiver', to: 'receiverState', status: 'OK', table: 'account' }));
|
|
143
143
|
runner.use(pipes.AntiLandAttack({ senderState: 'senderState', receiverState: 'receiverState' }));
|
|
144
144
|
runner.use(pipes.VerifyBlocked({ stateKeys: ['senderState', 'receiverState'] }));
|
|
145
|
+
runner.use(
|
|
146
|
+
pipes.verifyTokenAccess({
|
|
147
|
+
statesKey: 'tokenStates',
|
|
148
|
+
listFieldKey: 'spenders',
|
|
149
|
+
accountKeys: ['senderState', 'receiverState', 'itx.to'],
|
|
150
|
+
errorMessage: 'Account {address} is not allowed to transfer token {tokenAddress}',
|
|
151
|
+
})
|
|
152
|
+
);
|
|
145
153
|
|
|
146
154
|
runner.use(pipes.ExtractState({ from: 'assets', to: 'assetStates', status: 'INVALID_ASSET', table: 'asset' }));
|
|
147
155
|
runner.use(pipes.VerifyTransferrable({ assets: 'assetStates' }));
|
|
@@ -134,6 +134,14 @@ runner.use(pipes.VerifyAccountMigration({ signerKey: 'signerStates', stateKey: '
|
|
|
134
134
|
// 5. verify token state and balance
|
|
135
135
|
runner.use(pipes.ExtractState({ from: 'inputTokens', to: 'tokenStates', status: 'INVALID_TOKEN', table: 'token' }));
|
|
136
136
|
runner.use(pipes.VerifyTokenBalance({ ownerKey: 'signerStates', conditionKey: 'inputs' }));
|
|
137
|
+
runner.use(
|
|
138
|
+
pipes.verifyTokenAccess({
|
|
139
|
+
statesKey: 'tokenStates',
|
|
140
|
+
listFieldKey: 'spenders',
|
|
141
|
+
accountKeys: ['signerStates', 'receiverStates'],
|
|
142
|
+
errorMessage: 'Account {address} is not allowed to transfer token {tokenAddress}',
|
|
143
|
+
})
|
|
144
|
+
);
|
|
137
145
|
|
|
138
146
|
// 6. verify asset state and transferrable, ownership
|
|
139
147
|
runner.use(pipes.ExtractState({ from: 'inputAssets', to: 'assetStates', status: 'INVALID_ASSET', table: 'asset' }));
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.27.
|
|
6
|
+
"version": "1.27.8",
|
|
7
7
|
"description": "Predefined tx pipeline sets to execute certain type of transactions",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -19,20 +19,20 @@
|
|
|
19
19
|
"empty-value": "^1.0.1",
|
|
20
20
|
"lodash": "^4.17.21",
|
|
21
21
|
"url-join": "^4.0.1",
|
|
22
|
-
"@arcblock/did
|
|
23
|
-
"@arcblock/
|
|
24
|
-
"@arcblock/
|
|
25
|
-
"@arcblock/
|
|
26
|
-
"@
|
|
27
|
-
"@ocap/
|
|
28
|
-
"@ocap/mcrypto": "1.27.
|
|
29
|
-
"@ocap/
|
|
30
|
-
"@ocap/message": "1.27.
|
|
31
|
-
"@ocap/state": "1.27.
|
|
32
|
-
"@ocap/tx-pipeline": "1.27.
|
|
33
|
-
"@ocap/
|
|
34
|
-
"@ocap/
|
|
35
|
-
"@
|
|
22
|
+
"@arcblock/did": "1.27.8",
|
|
23
|
+
"@arcblock/did-util": "1.27.8",
|
|
24
|
+
"@arcblock/jwt": "1.27.8",
|
|
25
|
+
"@arcblock/validator": "1.27.8",
|
|
26
|
+
"@arcblock/vc": "1.27.8",
|
|
27
|
+
"@ocap/asset": "1.27.8",
|
|
28
|
+
"@ocap/mcrypto": "1.27.8",
|
|
29
|
+
"@ocap/client": "1.27.8",
|
|
30
|
+
"@ocap/message": "1.27.8",
|
|
31
|
+
"@ocap/state": "1.27.8",
|
|
32
|
+
"@ocap/tx-pipeline": "1.27.8",
|
|
33
|
+
"@ocap/merkle-tree": "1.27.8",
|
|
34
|
+
"@ocap/util": "1.27.8",
|
|
35
|
+
"@ocap/wallet": "1.27.8"
|
|
36
36
|
},
|
|
37
37
|
"resolutions": {
|
|
38
38
|
"bn.js": "5.2.2",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"jest": "^29.7.0",
|
|
43
43
|
"start-server-and-test": "^1.14.0",
|
|
44
|
-
"@ocap/e2e-test": "1.27.
|
|
45
|
-
"@ocap/statedb-memory": "1.27.
|
|
44
|
+
"@ocap/e2e-test": "1.27.8",
|
|
45
|
+
"@ocap/statedb-memory": "1.27.8"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
48
|
"lint": "eslint tests lib",
|