@velocitycareerlabs/verification-coupon-contract 1.25.0-dev-build.12642c864
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/.openzeppelin/unknown-1480.json +2156 -0
- package/.openzeppelin/unknown-1481.json +1704 -0
- package/.openzeppelin/unknown-1482.json +1704 -0
- package/.openzeppelin/unknown-1483.json +927 -0
- package/LICENSE +248 -0
- package/build/contracts/Migrations.json +1109 -0
- package/contracts/Migrations.sol +18 -0
- package/contracts/VerificationCoupon.sol +147 -0
- package/migration-status.js +41 -0
- package/migrations/10_vl-3756-emit-burn-time.js +17 -0
- package/migrations/11_vl-5092-new-permissions.js +21 -0
- package/migrations/12_vl-8185-allow-unknown-types.js +21 -0
- package/migrations/1_initial_migration.js +5 -0
- package/migrations/2_deploy_contract.js +9 -0
- package/migrations/3_deploy_contract_ERC_1155.js +9 -0
- package/migrations/4_set_balances.js +65 -0
- package/migrations/5_remove_tx_origin.js +19 -0
- package/migrations/6_vl-2898-operator_burn.js +17 -0
- package/migrations/7_vl-2898-fix-get-token-id-operator.js +17 -0
- package/migrations/8_vl-4587-get-token-id-expired-behavior.js +17 -0
- package/migrations/9_vl-3994-emit-balance-expiration.js +17 -0
- package/migrations/contracts-v1/metadata-registry.json +29674 -0
- package/migrations/contracts-v1/revocation-registry.json +19060 -0
- package/migrations/contracts-v1/verification-coupon.json +31724 -0
- package/package.json +32 -0
- package/test/verification-coupon.test.js +637 -0
- package/truffle-config.js +25 -0
package/package.json
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
{
|
2
|
+
"name": "@velocitycareerlabs/verification-coupon-contract",
|
3
|
+
"version": "1.25.0-dev-build.12642c864",
|
4
|
+
"description": "",
|
5
|
+
"main": "index.js",
|
6
|
+
"scripts": {
|
7
|
+
"test": "truffle test --migrate-none",
|
8
|
+
"build": "truffle compile --all",
|
9
|
+
"migrate:dev": "truffle migrate --network dev",
|
10
|
+
"migrate:qa": "truffle migrate --network qa",
|
11
|
+
"migrate:staging": "truffle migrate --network staging",
|
12
|
+
"migrate:prod": "truffle migrate --network prod"
|
13
|
+
},
|
14
|
+
"author": "",
|
15
|
+
"license": "Apache-2.0",
|
16
|
+
"publishConfig": {
|
17
|
+
"access": "public"
|
18
|
+
},
|
19
|
+
"dependencies": {
|
20
|
+
"@openzeppelin/contracts": "4.9.6",
|
21
|
+
"@openzeppelin/contracts-upgradeable": "4.9.6",
|
22
|
+
"@openzeppelin/truffle-upgrades": "^1.11.0",
|
23
|
+
"@truffle/hdwallet-provider": "^1.7.0",
|
24
|
+
"@velocitycareerlabs/metadata-registry-contract": "1.25.0-dev-build.12642c864",
|
25
|
+
"truffle-assertions": "^0.9.2",
|
26
|
+
"web3": "^1.3.6"
|
27
|
+
},
|
28
|
+
"devDependencies": {
|
29
|
+
"truffle": "5.11.5"
|
30
|
+
},
|
31
|
+
"gitHead": "ca5028aa212670198151cabf1147bacf59f10851"
|
32
|
+
}
|
@@ -0,0 +1,637 @@
|
|
1
|
+
const truffleAssert = require('truffle-assertions');
|
2
|
+
|
3
|
+
const VerificationCoupon = artifacts.require(
|
4
|
+
'../contracts/VerificationCoupon.sol'
|
5
|
+
);
|
6
|
+
const Permissions = artifacts.require(
|
7
|
+
'../../permissions/contracts/Permissions.sol'
|
8
|
+
);
|
9
|
+
|
10
|
+
const setupContracts = async (metadataContractAddress, primary) => {
|
11
|
+
const permissionsContractInstance = await Permissions.new();
|
12
|
+
await permissionsContractInstance.initialize();
|
13
|
+
const verificationCouponInstance = await VerificationCoupon.new();
|
14
|
+
await verificationCouponInstance.initialize(
|
15
|
+
'Velocity Verification Coupon',
|
16
|
+
'https://www.velocitynetwork.foundation/'
|
17
|
+
);
|
18
|
+
await verificationCouponInstance.setPermissionsAddress(
|
19
|
+
permissionsContractInstance.address
|
20
|
+
);
|
21
|
+
await permissionsContractInstance.addAddressScope(
|
22
|
+
metadataContractAddress,
|
23
|
+
'coupon:burn'
|
24
|
+
);
|
25
|
+
await permissionsContractInstance.addPrimary(primary, primary, primary);
|
26
|
+
await permissionsContractInstance.addAddressScope(
|
27
|
+
primary,
|
28
|
+
'transactions:write'
|
29
|
+
);
|
30
|
+
return { permissionsContractInstance, verificationCouponInstance };
|
31
|
+
};
|
32
|
+
|
33
|
+
describe('VerificationCoupon Contract Test Suite', () => {
|
34
|
+
const oneDaySeconds = 60 * 60 * 24;
|
35
|
+
const expirationTime = Math.floor(Date.now() / 1000) + 30 * oneDaySeconds;
|
36
|
+
const expiredTime = expirationTime - 60 * oneDaySeconds;
|
37
|
+
const traceId = 'trackingId';
|
38
|
+
const caoDid = 'did:velocity:42';
|
39
|
+
const burnerDid = 'did:velocity:456';
|
40
|
+
const ownerDid = 'did:velocity:456';
|
41
|
+
|
42
|
+
contract('VerificationCoupon', (accounts) => {
|
43
|
+
const deployerAccount = accounts[0];
|
44
|
+
const tokenOwner = accounts[1];
|
45
|
+
const primaryAccount = tokenOwner;
|
46
|
+
const permissionsAccount = primaryAccount;
|
47
|
+
const operatorAccount = accounts[2];
|
48
|
+
const mockMetadataContractAddress = accounts[3];
|
49
|
+
let permissionsContractInstance;
|
50
|
+
let verificationCouponInstance;
|
51
|
+
before(async () => {
|
52
|
+
({ permissionsContractInstance, verificationCouponInstance } =
|
53
|
+
await setupContracts(mockMetadataContractAddress, primaryAccount));
|
54
|
+
await permissionsContractInstance.addOperatorKey(
|
55
|
+
primaryAccount,
|
56
|
+
operatorAccount,
|
57
|
+
{ from: permissionsAccount }
|
58
|
+
);
|
59
|
+
});
|
60
|
+
describe('constructor', () => {
|
61
|
+
it('Verification Coupon contract should be correctly deployed', async () => {
|
62
|
+
assert.equal(
|
63
|
+
await verificationCouponInstance._getTokenName(),
|
64
|
+
'Velocity Verification Coupon',
|
65
|
+
'value was not ok'
|
66
|
+
);
|
67
|
+
});
|
68
|
+
it('Broker role is currently', async () => {
|
69
|
+
assert.equal(
|
70
|
+
await verificationCouponInstance.MINTER_ROLE(),
|
71
|
+
'0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6',
|
72
|
+
'value was not ok'
|
73
|
+
);
|
74
|
+
});
|
75
|
+
});
|
76
|
+
describe('Mint new token', () => {
|
77
|
+
const quantity = 3;
|
78
|
+
it('New token minted with id 0 & 1 by Contract owner', async () => {
|
79
|
+
await verificationCouponInstance.mint(
|
80
|
+
tokenOwner,
|
81
|
+
expirationTime,
|
82
|
+
quantity,
|
83
|
+
traceId,
|
84
|
+
ownerDid,
|
85
|
+
{
|
86
|
+
from: deployerAccount,
|
87
|
+
}
|
88
|
+
);
|
89
|
+
await verificationCouponInstance.mint(
|
90
|
+
tokenOwner,
|
91
|
+
expirationTime,
|
92
|
+
quantity,
|
93
|
+
traceId,
|
94
|
+
ownerDid,
|
95
|
+
{
|
96
|
+
from: deployerAccount,
|
97
|
+
}
|
98
|
+
);
|
99
|
+
});
|
100
|
+
it('Burn token with id 0 of token owner by operator via metadata contract', async () => {
|
101
|
+
await verificationCouponInstance.burn(
|
102
|
+
0,
|
103
|
+
traceId,
|
104
|
+
caoDid,
|
105
|
+
burnerDid,
|
106
|
+
operatorAccount,
|
107
|
+
{
|
108
|
+
from: mockMetadataContractAddress,
|
109
|
+
}
|
110
|
+
);
|
111
|
+
});
|
112
|
+
it('Burn token with id 0 of token owner by non-operator via metadata contract fails', async () => {
|
113
|
+
await truffleAssert.fails(
|
114
|
+
verificationCouponInstance.burn(
|
115
|
+
0,
|
116
|
+
traceId,
|
117
|
+
caoDid,
|
118
|
+
burnerDid,
|
119
|
+
primaryAccount,
|
120
|
+
{
|
121
|
+
from: mockMetadataContractAddress,
|
122
|
+
}
|
123
|
+
),
|
124
|
+
'Permissions: operator not pointing to a primary'
|
125
|
+
);
|
126
|
+
});
|
127
|
+
it('Burn token with id 0 by contract deployer fails', async () => {
|
128
|
+
await truffleAssert.fails(
|
129
|
+
verificationCouponInstance.burn(
|
130
|
+
0,
|
131
|
+
traceId,
|
132
|
+
caoDid,
|
133
|
+
burnerDid,
|
134
|
+
operatorAccount,
|
135
|
+
{
|
136
|
+
from: deployerAccount,
|
137
|
+
}
|
138
|
+
),
|
139
|
+
'Burn: caller does not have coupon:burn permission'
|
140
|
+
);
|
141
|
+
});
|
142
|
+
it('Burn token with id 1 by any account without permission - rejected!', async () => {
|
143
|
+
const accountWithoutTokens = accounts[4];
|
144
|
+
await truffleAssert.fails(
|
145
|
+
verificationCouponInstance.burn(
|
146
|
+
0,
|
147
|
+
traceId,
|
148
|
+
caoDid,
|
149
|
+
burnerDid,
|
150
|
+
accountWithoutTokens,
|
151
|
+
{ from: accountWithoutTokens }
|
152
|
+
),
|
153
|
+
'Burn: caller does not have coupon:burn permission'
|
154
|
+
);
|
155
|
+
});
|
156
|
+
});
|
157
|
+
});
|
158
|
+
|
159
|
+
contract('VerificationCoupon', (accounts) => {
|
160
|
+
const deployerAccount = accounts[0];
|
161
|
+
const issuer = accounts[2];
|
162
|
+
const primaryAccount = issuer;
|
163
|
+
const permissionsAccount = primaryAccount;
|
164
|
+
const operatorAccount = accounts[3];
|
165
|
+
|
166
|
+
const mockMetadataContractAddress = accounts[4];
|
167
|
+
const quantity = 1;
|
168
|
+
let permissionsContractInstance;
|
169
|
+
let verificationCouponInstance;
|
170
|
+
|
171
|
+
before(async () => {
|
172
|
+
({ permissionsContractInstance, verificationCouponInstance } =
|
173
|
+
await setupContracts(mockMetadataContractAddress, primaryAccount));
|
174
|
+
await permissionsContractInstance.addOperatorKey(
|
175
|
+
primaryAccount,
|
176
|
+
operatorAccount,
|
177
|
+
{ from: permissionsAccount }
|
178
|
+
);
|
179
|
+
});
|
180
|
+
|
181
|
+
describe('Check contract owner rights', () => {
|
182
|
+
it("Account don't have minter role", async () => {
|
183
|
+
assert.notEqual(
|
184
|
+
await verificationCouponInstance.hasRole(
|
185
|
+
'0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6',
|
186
|
+
issuer
|
187
|
+
),
|
188
|
+
'this account has minter rights'
|
189
|
+
);
|
190
|
+
});
|
191
|
+
it('Add new minter', async () => {
|
192
|
+
await verificationCouponInstance.grantRole(
|
193
|
+
'0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6',
|
194
|
+
issuer,
|
195
|
+
{ from: deployerAccount }
|
196
|
+
);
|
197
|
+
assert.equal(
|
198
|
+
await verificationCouponInstance.hasRole(
|
199
|
+
'0x9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a6',
|
200
|
+
issuer
|
201
|
+
),
|
202
|
+
true,
|
203
|
+
'Account dont have minter role'
|
204
|
+
);
|
205
|
+
});
|
206
|
+
it('VNF burn token created by allowed account', async () => {
|
207
|
+
await verificationCouponInstance.mint(
|
208
|
+
issuer,
|
209
|
+
expirationTime,
|
210
|
+
quantity,
|
211
|
+
traceId,
|
212
|
+
ownerDid,
|
213
|
+
{ from: issuer }
|
214
|
+
);
|
215
|
+
await verificationCouponInstance.burn(
|
216
|
+
0,
|
217
|
+
traceId,
|
218
|
+
caoDid,
|
219
|
+
burnerDid,
|
220
|
+
operatorAccount,
|
221
|
+
{ from: mockMetadataContractAddress }
|
222
|
+
);
|
223
|
+
});
|
224
|
+
});
|
225
|
+
describe('Token Info', () => {
|
226
|
+
it('Token expired', async () => {
|
227
|
+
await verificationCouponInstance.mint(
|
228
|
+
issuer,
|
229
|
+
expiredTime,
|
230
|
+
quantity,
|
231
|
+
traceId,
|
232
|
+
ownerDid,
|
233
|
+
{ from: issuer }
|
234
|
+
);
|
235
|
+
assert.equal(
|
236
|
+
await verificationCouponInstance.isExpired(1),
|
237
|
+
true,
|
238
|
+
'Token expired'
|
239
|
+
);
|
240
|
+
});
|
241
|
+
it('Token not expired', async () => {
|
242
|
+
await verificationCouponInstance.mint(
|
243
|
+
issuer,
|
244
|
+
expirationTime,
|
245
|
+
quantity,
|
246
|
+
traceId,
|
247
|
+
ownerDid,
|
248
|
+
{ from: issuer }
|
249
|
+
);
|
250
|
+
assert.equal(
|
251
|
+
await verificationCouponInstance.isExpired(2),
|
252
|
+
false,
|
253
|
+
'Token actual'
|
254
|
+
);
|
255
|
+
});
|
256
|
+
});
|
257
|
+
});
|
258
|
+
|
259
|
+
contract('VerificationCoupon', (accounts) => {
|
260
|
+
const deployerAccount = accounts[0];
|
261
|
+
const primaryAccount = accounts[1];
|
262
|
+
const permissionsAccount = primaryAccount;
|
263
|
+
const operatorAccount = accounts[2];
|
264
|
+
const mockMetadataContractAddress = accounts[3];
|
265
|
+
let permissionsContractInstance;
|
266
|
+
let verificationCouponInstance;
|
267
|
+
|
268
|
+
before(async () => {
|
269
|
+
({ permissionsContractInstance, verificationCouponInstance } =
|
270
|
+
await setupContracts(mockMetadataContractAddress, primaryAccount));
|
271
|
+
await permissionsContractInstance.addOperatorKey(
|
272
|
+
primaryAccount,
|
273
|
+
operatorAccount,
|
274
|
+
{ from: permissionsAccount }
|
275
|
+
);
|
276
|
+
});
|
277
|
+
describe('Mint new token bundle', () => {
|
278
|
+
const quantity = 3;
|
279
|
+
it('New token bundle minted', async () => {
|
280
|
+
const result = await verificationCouponInstance.mint(
|
281
|
+
primaryAccount,
|
282
|
+
expirationTime,
|
283
|
+
quantity,
|
284
|
+
traceId,
|
285
|
+
ownerDid,
|
286
|
+
{
|
287
|
+
from: deployerAccount,
|
288
|
+
}
|
289
|
+
);
|
290
|
+
|
291
|
+
truffleAssert.eventEmitted(result, 'MintCouponBundle');
|
292
|
+
});
|
293
|
+
|
294
|
+
it('Burn token from new bundle by operator account', async () => {
|
295
|
+
await verificationCouponInstance.burn(
|
296
|
+
0,
|
297
|
+
traceId,
|
298
|
+
caoDid,
|
299
|
+
burnerDid,
|
300
|
+
operatorAccount,
|
301
|
+
{ from: mockMetadataContractAddress }
|
302
|
+
);
|
303
|
+
await verificationCouponInstance.burn(
|
304
|
+
0,
|
305
|
+
traceId,
|
306
|
+
caoDid,
|
307
|
+
burnerDid,
|
308
|
+
operatorAccount,
|
309
|
+
{ from: mockMetadataContractAddress }
|
310
|
+
);
|
311
|
+
const tx = await verificationCouponInstance.burn(
|
312
|
+
0,
|
313
|
+
traceId,
|
314
|
+
caoDid,
|
315
|
+
burnerDid,
|
316
|
+
operatorAccount,
|
317
|
+
{ from: mockMetadataContractAddress }
|
318
|
+
);
|
319
|
+
const { blockNumber } = tx.logs[1];
|
320
|
+
const block = await web3.eth.getBlock(blockNumber);
|
321
|
+
truffleAssert.eventEmitted(tx, 'BurnCoupon', {
|
322
|
+
owner: primaryAccount,
|
323
|
+
bundleId: web3.utils.toBN('0'),
|
324
|
+
balance: web3.utils.toBN('0'),
|
325
|
+
expirationTime: web3.utils.toBN(expirationTime),
|
326
|
+
burnTime: web3.utils.toBN(block.timestamp),
|
327
|
+
});
|
328
|
+
});
|
329
|
+
|
330
|
+
it('Throw an error if quantity is invalid', async () => {
|
331
|
+
await truffleAssert.fails(
|
332
|
+
verificationCouponInstance.mint(
|
333
|
+
primaryAccount,
|
334
|
+
expirationTime,
|
335
|
+
0,
|
336
|
+
traceId,
|
337
|
+
ownerDid,
|
338
|
+
{
|
339
|
+
from: deployerAccount,
|
340
|
+
}
|
341
|
+
),
|
342
|
+
'Invalid quantity'
|
343
|
+
);
|
344
|
+
});
|
345
|
+
});
|
346
|
+
|
347
|
+
describe('Get tokens', () => {
|
348
|
+
const quantity = 3;
|
349
|
+
const secondCouponId = 1;
|
350
|
+
it('Get the unused coupon for the account', async () => {
|
351
|
+
await verificationCouponInstance.mint(
|
352
|
+
primaryAccount,
|
353
|
+
expirationTime,
|
354
|
+
quantity,
|
355
|
+
traceId,
|
356
|
+
ownerDid,
|
357
|
+
{
|
358
|
+
from: deployerAccount,
|
359
|
+
}
|
360
|
+
);
|
361
|
+
await verificationCouponInstance.mint(
|
362
|
+
primaryAccount,
|
363
|
+
expirationTime,
|
364
|
+
quantity,
|
365
|
+
traceId,
|
366
|
+
ownerDid,
|
367
|
+
{
|
368
|
+
from: deployerAccount,
|
369
|
+
}
|
370
|
+
);
|
371
|
+
const couponId = await verificationCouponInstance.getTokenId(
|
372
|
+
operatorAccount
|
373
|
+
);
|
374
|
+
assert.equal(
|
375
|
+
couponId.toNumber(),
|
376
|
+
secondCouponId,
|
377
|
+
'It is not the first unused coupon!'
|
378
|
+
);
|
379
|
+
});
|
380
|
+
|
381
|
+
it('Get the next unused coupon when the previous was burned', async () => {
|
382
|
+
await verificationCouponInstance.mint(
|
383
|
+
primaryAccount,
|
384
|
+
expirationTime,
|
385
|
+
quantity,
|
386
|
+
traceId,
|
387
|
+
ownerDid,
|
388
|
+
{
|
389
|
+
from: deployerAccount,
|
390
|
+
}
|
391
|
+
);
|
392
|
+
const tokenIds = [1, 1, 1, 2, 2, 2, 3, 3, 3];
|
393
|
+
for (let i = 0; i < tokenIds.length; i += 1) {
|
394
|
+
const couponId = await verificationCouponInstance.getTokenId(
|
395
|
+
operatorAccount
|
396
|
+
);
|
397
|
+
await verificationCouponInstance.burn(
|
398
|
+
couponId,
|
399
|
+
traceId,
|
400
|
+
caoDid,
|
401
|
+
burnerDid,
|
402
|
+
operatorAccount,
|
403
|
+
{
|
404
|
+
from: mockMetadataContractAddress,
|
405
|
+
}
|
406
|
+
);
|
407
|
+
assert.equal(
|
408
|
+
couponId.toNumber(),
|
409
|
+
tokenIds[i],
|
410
|
+
'It is not the first unused coupon!'
|
411
|
+
);
|
412
|
+
}
|
413
|
+
});
|
414
|
+
it('Mint and burn one token in loop', async () => {
|
415
|
+
for (let i = 4; i < 9; i += 1) {
|
416
|
+
await verificationCouponInstance.mint(
|
417
|
+
primaryAccount,
|
418
|
+
expirationTime,
|
419
|
+
1,
|
420
|
+
traceId,
|
421
|
+
ownerDid,
|
422
|
+
{
|
423
|
+
from: deployerAccount,
|
424
|
+
}
|
425
|
+
);
|
426
|
+
const couponId = await verificationCouponInstance.getTokenId(
|
427
|
+
operatorAccount
|
428
|
+
);
|
429
|
+
await verificationCouponInstance.burn(
|
430
|
+
couponId,
|
431
|
+
traceId,
|
432
|
+
caoDid,
|
433
|
+
burnerDid,
|
434
|
+
operatorAccount,
|
435
|
+
{
|
436
|
+
from: mockMetadataContractAddress,
|
437
|
+
}
|
438
|
+
);
|
439
|
+
assert.equal(
|
440
|
+
couponId.toNumber(),
|
441
|
+
i,
|
442
|
+
'It is not the first unused coupon!'
|
443
|
+
);
|
444
|
+
}
|
445
|
+
});
|
446
|
+
|
447
|
+
it('Mint and burn one by one and ignore expired minting', async () => {
|
448
|
+
const tokenIds = [9, 11, 13];
|
449
|
+
|
450
|
+
for (let i = 0; i < 3; i += 1) {
|
451
|
+
await verificationCouponInstance.mint(
|
452
|
+
primaryAccount,
|
453
|
+
expirationTime,
|
454
|
+
1,
|
455
|
+
traceId,
|
456
|
+
ownerDid,
|
457
|
+
{
|
458
|
+
from: deployerAccount,
|
459
|
+
}
|
460
|
+
);
|
461
|
+
await verificationCouponInstance.mint(
|
462
|
+
primaryAccount,
|
463
|
+
expiredTime,
|
464
|
+
1,
|
465
|
+
traceId,
|
466
|
+
ownerDid,
|
467
|
+
{
|
468
|
+
from: deployerAccount,
|
469
|
+
}
|
470
|
+
);
|
471
|
+
const couponId = await verificationCouponInstance.getTokenId(
|
472
|
+
operatorAccount
|
473
|
+
);
|
474
|
+
await verificationCouponInstance.burn(
|
475
|
+
couponId,
|
476
|
+
traceId,
|
477
|
+
caoDid,
|
478
|
+
burnerDid,
|
479
|
+
operatorAccount,
|
480
|
+
{
|
481
|
+
from: mockMetadataContractAddress,
|
482
|
+
}
|
483
|
+
);
|
484
|
+
assert.equal(
|
485
|
+
couponId.toNumber(),
|
486
|
+
tokenIds[i],
|
487
|
+
'It is not the first unused coupon!'
|
488
|
+
);
|
489
|
+
}
|
490
|
+
});
|
491
|
+
|
492
|
+
it('Throw an error if the account without tokens', async () => {
|
493
|
+
await truffleAssert.fails(
|
494
|
+
verificationCouponInstance.getTokenId(operatorAccount),
|
495
|
+
'No available tokens'
|
496
|
+
);
|
497
|
+
});
|
498
|
+
|
499
|
+
it('Error if primary tries to retrieve its own tokens, not as an operator', async () => {
|
500
|
+
await truffleAssert.fails(
|
501
|
+
verificationCouponInstance.getTokenId(primaryAccount),
|
502
|
+
'Permissions: operator not pointing to a primary'
|
503
|
+
);
|
504
|
+
});
|
505
|
+
});
|
506
|
+
});
|
507
|
+
|
508
|
+
contract('VerificationCoupon', (accounts) => {
|
509
|
+
const deployerAccount = accounts[0];
|
510
|
+
const primaryAccount = accounts[1];
|
511
|
+
const permissionsAccount = primaryAccount;
|
512
|
+
const operatorAccount = accounts[2];
|
513
|
+
const mockMetadataContractAddress = accounts[3];
|
514
|
+
let permissionsContractInstance;
|
515
|
+
let verificationCouponInstance;
|
516
|
+
|
517
|
+
before(async () => {
|
518
|
+
({ permissionsContractInstance, verificationCouponInstance } =
|
519
|
+
await setupContracts(mockMetadataContractAddress, primaryAccount));
|
520
|
+
await permissionsContractInstance.addOperatorKey(
|
521
|
+
primaryAccount,
|
522
|
+
operatorAccount,
|
523
|
+
{ from: permissionsAccount }
|
524
|
+
);
|
525
|
+
});
|
526
|
+
describe('Burn expired tokens', () => {
|
527
|
+
const quantity = 100;
|
528
|
+
const firstCouponId = 2;
|
529
|
+
it('Burn two expired bundles', async () => {
|
530
|
+
await verificationCouponInstance.mint(
|
531
|
+
primaryAccount,
|
532
|
+
expiredTime,
|
533
|
+
quantity,
|
534
|
+
traceId,
|
535
|
+
ownerDid,
|
536
|
+
{
|
537
|
+
from: deployerAccount,
|
538
|
+
}
|
539
|
+
);
|
540
|
+
await verificationCouponInstance.mint(
|
541
|
+
primaryAccount,
|
542
|
+
expiredTime,
|
543
|
+
quantity,
|
544
|
+
traceId,
|
545
|
+
ownerDid,
|
546
|
+
{
|
547
|
+
from: deployerAccount,
|
548
|
+
}
|
549
|
+
);
|
550
|
+
await verificationCouponInstance.mint(
|
551
|
+
primaryAccount,
|
552
|
+
expirationTime,
|
553
|
+
quantity,
|
554
|
+
traceId,
|
555
|
+
ownerDid,
|
556
|
+
{
|
557
|
+
from: deployerAccount,
|
558
|
+
}
|
559
|
+
);
|
560
|
+
const couponId = await verificationCouponInstance.getTokenId(
|
561
|
+
operatorAccount
|
562
|
+
);
|
563
|
+
|
564
|
+
assert.equal(
|
565
|
+
couponId.toNumber(),
|
566
|
+
firstCouponId,
|
567
|
+
'It is not the first unused coupon!'
|
568
|
+
);
|
569
|
+
assert.equal(
|
570
|
+
await verificationCouponInstance.balanceOf(primaryAccount, 0),
|
571
|
+
100
|
572
|
+
);
|
573
|
+
assert.equal(
|
574
|
+
await verificationCouponInstance.balanceOf(primaryAccount, 1),
|
575
|
+
100
|
576
|
+
);
|
577
|
+
await verificationCouponInstance.burn(
|
578
|
+
firstCouponId,
|
579
|
+
traceId,
|
580
|
+
caoDid,
|
581
|
+
burnerDid,
|
582
|
+
operatorAccount,
|
583
|
+
{
|
584
|
+
from: mockMetadataContractAddress,
|
585
|
+
}
|
586
|
+
);
|
587
|
+
assert.equal(
|
588
|
+
await verificationCouponInstance.balanceOf(primaryAccount, 0),
|
589
|
+
0
|
590
|
+
);
|
591
|
+
assert.equal(
|
592
|
+
await verificationCouponInstance.balanceOf(primaryAccount, 1),
|
593
|
+
0
|
594
|
+
);
|
595
|
+
});
|
596
|
+
});
|
597
|
+
});
|
598
|
+
|
599
|
+
describe('getTokenId handle token found, but all tokens are expired', () => {
|
600
|
+
contract('VerificationCoupon', (accounts) => {
|
601
|
+
const deployerAccount = accounts[0];
|
602
|
+
const primaryAccount = accounts[1];
|
603
|
+
const permissionsAccount = primaryAccount;
|
604
|
+
const operatorAccount = accounts[2];
|
605
|
+
const mockMetadataContractAddress = accounts[3];
|
606
|
+
let permissionsContractInstance;
|
607
|
+
let verificationCouponInstance;
|
608
|
+
|
609
|
+
before(async () => {
|
610
|
+
({ permissionsContractInstance, verificationCouponInstance } =
|
611
|
+
await setupContracts(mockMetadataContractAddress, primaryAccount));
|
612
|
+
await permissionsContractInstance.addOperatorKey(
|
613
|
+
primaryAccount,
|
614
|
+
operatorAccount,
|
615
|
+
{ from: permissionsAccount }
|
616
|
+
);
|
617
|
+
});
|
618
|
+
it('should error when no unexpired tokens exist', async () => {
|
619
|
+
const quantity = 1;
|
620
|
+
await verificationCouponInstance.mint(
|
621
|
+
primaryAccount,
|
622
|
+
expiredTime,
|
623
|
+
quantity,
|
624
|
+
traceId,
|
625
|
+
ownerDid,
|
626
|
+
{
|
627
|
+
from: deployerAccount,
|
628
|
+
}
|
629
|
+
);
|
630
|
+
await truffleAssert.fails(
|
631
|
+
verificationCouponInstance.getTokenId(operatorAccount),
|
632
|
+
'No available tokens'
|
633
|
+
);
|
634
|
+
});
|
635
|
+
});
|
636
|
+
});
|
637
|
+
});
|
@@ -0,0 +1,25 @@
|
|
1
|
+
const PrivateKeyProvider = require('@truffle/hdwallet-provider');
|
2
|
+
const Web3HttpProvider = require('web3-providers-http');
|
3
|
+
|
4
|
+
const options = {
|
5
|
+
timeout: 120000,
|
6
|
+
};
|
7
|
+
|
8
|
+
module.exports = {
|
9
|
+
networks: {},
|
10
|
+
compilers: {
|
11
|
+
solc: {
|
12
|
+
version: '0.8.4', // Fetch exact version from solc-bin (default: truffle's version)
|
13
|
+
settings: {
|
14
|
+
// See the solidity docs for advice about optimization and evmVersion
|
15
|
+
optimizer: {
|
16
|
+
enabled: true,
|
17
|
+
runs: 200,
|
18
|
+
},
|
19
|
+
},
|
20
|
+
},
|
21
|
+
},
|
22
|
+
db: {
|
23
|
+
enabled: false,
|
24
|
+
},
|
25
|
+
};
|