mppx 0.3.5 → 0.3.7
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 +5 -4
- package/dist/BodyDigest.d.ts.map +1 -1
- package/dist/BodyDigest.js +2 -1
- package/dist/BodyDigest.js.map +1 -1
- package/dist/Challenge.d.ts.map +1 -1
- package/dist/Challenge.js +1 -9
- package/dist/Challenge.js.map +1 -1
- package/dist/internal/constantTimeEqual.d.ts +3 -0
- package/dist/internal/constantTimeEqual.d.ts.map +1 -0
- package/dist/internal/constantTimeEqual.js +10 -0
- package/dist/internal/constantTimeEqual.js.map +1 -0
- package/dist/internal/types.d.ts +10 -0
- package/dist/internal/types.d.ts.map +1 -1
- package/dist/proxy/internal/Headers.d.ts +2 -0
- package/dist/proxy/internal/Headers.d.ts.map +1 -1
- package/dist/proxy/internal/Headers.js +2 -0
- package/dist/proxy/internal/Headers.js.map +1 -1
- package/dist/proxy/internal/Route.d.ts +4 -0
- package/dist/proxy/internal/Route.d.ts.map +1 -1
- package/dist/proxy/internal/Route.js +4 -0
- package/dist/proxy/internal/Route.js.map +1 -1
- package/dist/server/NodeListener.d.ts +6 -0
- package/dist/server/NodeListener.d.ts.map +1 -1
- package/dist/server/NodeListener.js +6 -0
- package/dist/server/NodeListener.js.map +1 -1
- package/dist/server/Response.d.ts +17 -0
- package/dist/server/Response.d.ts.map +1 -1
- package/dist/server/Response.js +17 -0
- package/dist/server/Response.js.map +1 -1
- package/dist/tempo/client/ChannelOps.js.map +1 -1
- package/dist/tempo/client/Charge.d.ts.map +1 -1
- package/dist/tempo/client/Charge.js +1 -0
- package/dist/tempo/client/Charge.js.map +1 -1
- package/dist/tempo/internal/defaults.d.ts +34 -8
- package/dist/tempo/internal/defaults.d.ts.map +1 -1
- package/dist/tempo/internal/defaults.js +30 -8
- package/dist/tempo/internal/defaults.js.map +1 -1
- package/dist/tempo/server/Charge.js +2 -2
- package/dist/tempo/server/Charge.js.map +1 -1
- package/dist/tempo/server/Session.d.ts.map +1 -1
- package/dist/tempo/server/Session.js +8 -3
- package/dist/tempo/server/Session.js.map +1 -1
- package/package.json +1 -1
- package/src/BodyDigest.ts +2 -1
- package/src/Challenge.ts +1 -8
- package/src/internal/constantTimeEqual.test.ts +46 -0
- package/src/internal/constantTimeEqual.ts +7 -0
- package/src/internal/types.ts +11 -0
- package/src/proxy/internal/Headers.ts +2 -0
- package/src/proxy/internal/Route.ts +4 -0
- package/src/server/NodeListener.ts +6 -0
- package/src/server/Response.ts +17 -0
- package/src/tempo/client/ChannelOps.ts +1 -1
- package/src/tempo/client/Charge.ts +1 -0
- package/src/tempo/internal/defaults.test.ts +94 -0
- package/src/tempo/internal/defaults.ts +41 -8
- package/src/tempo/server/Charge.test.ts +150 -0
- package/src/tempo/server/Charge.ts +2 -2
- package/src/tempo/server/Session.test.ts +189 -1
- package/src/tempo/server/Session.ts +8 -3
- package/src/tempo/session/Voucher.test.ts +46 -0
|
@@ -656,6 +656,156 @@ describe('tempo', () => {
|
|
|
656
656
|
})
|
|
657
657
|
})
|
|
658
658
|
|
|
659
|
+
describe('default currency resolution', () => {
|
|
660
|
+
test('mainnet (default) resolves to USDC', () => {
|
|
661
|
+
const method = tempo_server.charge({
|
|
662
|
+
getClient: () => client,
|
|
663
|
+
account: accounts[0].address,
|
|
664
|
+
})
|
|
665
|
+
expect((method.defaults as Record<string, unknown>)?.currency).toBe(
|
|
666
|
+
'0x20C000000000000000000000b9537d11c60E8b50',
|
|
667
|
+
)
|
|
668
|
+
})
|
|
669
|
+
|
|
670
|
+
test('testnet: true defaults to pathUSD', () => {
|
|
671
|
+
const method = tempo_server.charge({
|
|
672
|
+
getClient: () => client,
|
|
673
|
+
account: accounts[0].address,
|
|
674
|
+
testnet: true,
|
|
675
|
+
})
|
|
676
|
+
expect((method.defaults as Record<string, unknown>)?.currency).toBe(
|
|
677
|
+
'0x20c0000000000000000000000000000000000000',
|
|
678
|
+
)
|
|
679
|
+
})
|
|
680
|
+
|
|
681
|
+
test('unknown chain defaults to pathUSD', () => {
|
|
682
|
+
const method = tempo_server.charge({
|
|
683
|
+
getClient: () => client,
|
|
684
|
+
account: accounts[0].address,
|
|
685
|
+
chainId: 69420,
|
|
686
|
+
})
|
|
687
|
+
expect((method.defaults as Record<string, unknown>)?.currency).toBe(
|
|
688
|
+
'0x20c0000000000000000000000000000000000000',
|
|
689
|
+
)
|
|
690
|
+
})
|
|
691
|
+
|
|
692
|
+
test('explicit currency overrides default', () => {
|
|
693
|
+
const method = tempo_server.charge({
|
|
694
|
+
getClient: () => client,
|
|
695
|
+
account: accounts[0].address,
|
|
696
|
+
testnet: false,
|
|
697
|
+
currency: '0xcustom',
|
|
698
|
+
})
|
|
699
|
+
expect(method.defaults?.currency).toBe('0xcustom')
|
|
700
|
+
})
|
|
701
|
+
|
|
702
|
+
test('decimals defaults to 6', () => {
|
|
703
|
+
const method = tempo_server.charge({
|
|
704
|
+
getClient: () => client,
|
|
705
|
+
account: accounts[0].address,
|
|
706
|
+
})
|
|
707
|
+
expect((method.defaults as Record<string, unknown>)?.decimals).toBe(6)
|
|
708
|
+
})
|
|
709
|
+
|
|
710
|
+
test('challenge contains USDC currency (mainnet default)', async () => {
|
|
711
|
+
const handler = Mppx_server.create({
|
|
712
|
+
methods: [
|
|
713
|
+
tempo_server.charge({
|
|
714
|
+
getClient: () => client,
|
|
715
|
+
account: accounts[0].address,
|
|
716
|
+
}),
|
|
717
|
+
],
|
|
718
|
+
realm,
|
|
719
|
+
secretKey,
|
|
720
|
+
})
|
|
721
|
+
|
|
722
|
+
const result = await (handler.charge as Function)({ amount: '1' })(
|
|
723
|
+
new Request('https://example.com'),
|
|
724
|
+
)
|
|
725
|
+
expect(result.status).toBe(402)
|
|
726
|
+
if (result.status !== 402) throw new Error()
|
|
727
|
+
|
|
728
|
+
const challenge = Challenge.fromResponse(result.challenge, {
|
|
729
|
+
methods: [tempo_client.charge()],
|
|
730
|
+
})
|
|
731
|
+
expect(challenge.request.currency).toBe('0x20C000000000000000000000b9537d11c60E8b50')
|
|
732
|
+
})
|
|
733
|
+
|
|
734
|
+
test('challenge contains pathUSD currency when testnet: true', async () => {
|
|
735
|
+
const handler = Mppx_server.create({
|
|
736
|
+
methods: [
|
|
737
|
+
tempo_server.charge({
|
|
738
|
+
getClient: () => client,
|
|
739
|
+
account: accounts[0].address,
|
|
740
|
+
testnet: true,
|
|
741
|
+
}),
|
|
742
|
+
],
|
|
743
|
+
realm,
|
|
744
|
+
secretKey,
|
|
745
|
+
})
|
|
746
|
+
|
|
747
|
+
const result = await (handler.charge as Function)({ amount: '1', chainId: chain.id })(
|
|
748
|
+
new Request('https://example.com'),
|
|
749
|
+
)
|
|
750
|
+
expect(result.status).toBe(402)
|
|
751
|
+
if (result.status !== 402) throw new Error()
|
|
752
|
+
|
|
753
|
+
const challenge = Challenge.fromResponse(result.challenge, {
|
|
754
|
+
methods: [tempo_client.charge()],
|
|
755
|
+
})
|
|
756
|
+
expect(challenge.request.currency).toBe('0x20c0000000000000000000000000000000000000')
|
|
757
|
+
})
|
|
758
|
+
|
|
759
|
+
test('challenge contains pathUSD currency (unknown chain)', async () => {
|
|
760
|
+
const handler = Mppx_server.create({
|
|
761
|
+
methods: [
|
|
762
|
+
tempo_server.charge({
|
|
763
|
+
getClient: () => client,
|
|
764
|
+
account: accounts[0].address,
|
|
765
|
+
chainId: 69420,
|
|
766
|
+
}),
|
|
767
|
+
],
|
|
768
|
+
realm,
|
|
769
|
+
secretKey,
|
|
770
|
+
})
|
|
771
|
+
|
|
772
|
+
const result = await (handler.charge as Function)({ amount: '1' })(
|
|
773
|
+
new Request('https://example.com'),
|
|
774
|
+
)
|
|
775
|
+
expect(result.status).toBe(402)
|
|
776
|
+
if (result.status !== 402) throw new Error()
|
|
777
|
+
|
|
778
|
+
const challenge = Challenge.fromResponse(result.challenge, {
|
|
779
|
+
methods: [tempo_client.charge()],
|
|
780
|
+
})
|
|
781
|
+
expect(challenge.request.currency).toBe('0x20c0000000000000000000000000000000000000')
|
|
782
|
+
})
|
|
783
|
+
|
|
784
|
+
test('explicit currency in challenge overrides testnet default', async () => {
|
|
785
|
+
const handler = Mppx_server.create({
|
|
786
|
+
methods: [
|
|
787
|
+
tempo_server.charge({
|
|
788
|
+
getClient: () => client,
|
|
789
|
+
account: accounts[0].address,
|
|
790
|
+
testnet: false,
|
|
791
|
+
currency: asset,
|
|
792
|
+
}),
|
|
793
|
+
],
|
|
794
|
+
realm,
|
|
795
|
+
secretKey,
|
|
796
|
+
})
|
|
797
|
+
|
|
798
|
+
const result = await handler.charge({ amount: '1' })(new Request('https://example.com'))
|
|
799
|
+
expect(result.status).toBe(402)
|
|
800
|
+
if (result.status !== 402) throw new Error()
|
|
801
|
+
|
|
802
|
+
const challenge = Challenge.fromResponse(result.challenge, {
|
|
803
|
+
methods: [tempo_client.charge()],
|
|
804
|
+
})
|
|
805
|
+
expect(challenge.request.currency).toBe(asset)
|
|
806
|
+
})
|
|
807
|
+
})
|
|
808
|
+
|
|
659
809
|
describe('attribution memo', () => {
|
|
660
810
|
test('client always generates attribution memo (hash credential)', async () => {
|
|
661
811
|
const httpServer = await Http.createServer(async (req, res) => {
|
|
@@ -40,7 +40,7 @@ export function charge<const parameters extends charge.Parameters>(
|
|
|
40
40
|
) {
|
|
41
41
|
const {
|
|
42
42
|
amount,
|
|
43
|
-
currency,
|
|
43
|
+
currency = defaults.resolveCurrency(parameters),
|
|
44
44
|
decimals = defaults.decimals,
|
|
45
45
|
description,
|
|
46
46
|
externalId,
|
|
@@ -71,7 +71,7 @@ export function charge<const parameters extends charge.Parameters>(
|
|
|
71
71
|
async request({ credential, request }) {
|
|
72
72
|
const chainId = await (async () => {
|
|
73
73
|
if (request.chainId) return request.chainId
|
|
74
|
-
if (parameters.testnet) return defaults.
|
|
74
|
+
if (parameters.testnet) return defaults.chainId.testnet
|
|
75
75
|
return (await getClient({})).chain?.id
|
|
76
76
|
})()
|
|
77
77
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { z } from 'mppx'
|
|
2
|
+
import { Challenge } from 'mppx'
|
|
2
3
|
import { Mppx as Mppx_server, tempo as tempo_server } from 'mppx/server'
|
|
3
4
|
import { type Address, createClient, type Hex } from 'viem'
|
|
4
5
|
import { Addresses } from 'viem/tempo'
|
|
@@ -1334,6 +1335,193 @@ describe('monotonicity and TOCTOU (unit tests)', () => {
|
|
|
1334
1335
|
})
|
|
1335
1336
|
})
|
|
1336
1337
|
|
|
1338
|
+
describe('session default currency resolution', () => {
|
|
1339
|
+
const mockClient = createClient({ transport: http('http://localhost:1') })
|
|
1340
|
+
const mockMainnetClient = createClient({
|
|
1341
|
+
chain: {
|
|
1342
|
+
id: 4217,
|
|
1343
|
+
name: 'Tempo',
|
|
1344
|
+
nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 },
|
|
1345
|
+
rpcUrls: { default: { http: ['http://localhost:1'] } },
|
|
1346
|
+
},
|
|
1347
|
+
transport: http('http://localhost:1'),
|
|
1348
|
+
})
|
|
1349
|
+
const mockTestnetClient = createClient({
|
|
1350
|
+
chain: {
|
|
1351
|
+
id: 42431,
|
|
1352
|
+
name: 'Tempo Testnet',
|
|
1353
|
+
nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 },
|
|
1354
|
+
rpcUrls: { default: { http: ['http://localhost:1'] } },
|
|
1355
|
+
},
|
|
1356
|
+
transport: http('http://localhost:1'),
|
|
1357
|
+
})
|
|
1358
|
+
|
|
1359
|
+
test('mainnet (default) resolves to USDC', () => {
|
|
1360
|
+
const server = session({
|
|
1361
|
+
store: Store.memory(),
|
|
1362
|
+
getClient: () => mockClient,
|
|
1363
|
+
account: '0x0000000000000000000000000000000000000001',
|
|
1364
|
+
escrowContract: '0x0000000000000000000000000000000000000002',
|
|
1365
|
+
} as session.Parameters)
|
|
1366
|
+
expect(server.defaults?.currency).toBe('0x20C000000000000000000000b9537d11c60E8b50')
|
|
1367
|
+
})
|
|
1368
|
+
|
|
1369
|
+
test('testnet: true defaults to pathUSD', () => {
|
|
1370
|
+
const server = session({
|
|
1371
|
+
store: Store.memory(),
|
|
1372
|
+
getClient: () => mockClient,
|
|
1373
|
+
account: '0x0000000000000000000000000000000000000001',
|
|
1374
|
+
escrowContract: '0x0000000000000000000000000000000000000002',
|
|
1375
|
+
testnet: true,
|
|
1376
|
+
} as session.Parameters)
|
|
1377
|
+
expect(server.defaults?.currency).toBe('0x20c0000000000000000000000000000000000000')
|
|
1378
|
+
})
|
|
1379
|
+
|
|
1380
|
+
test('unknown chain defaults to pathUSD', () => {
|
|
1381
|
+
const server = session({
|
|
1382
|
+
store: Store.memory(),
|
|
1383
|
+
getClient: () => mockClient,
|
|
1384
|
+
account: '0x0000000000000000000000000000000000000001',
|
|
1385
|
+
escrowContract: '0x0000000000000000000000000000000000000002',
|
|
1386
|
+
chainId: 69420,
|
|
1387
|
+
} as session.Parameters)
|
|
1388
|
+
expect(server.defaults?.currency).toBe('0x20c0000000000000000000000000000000000000')
|
|
1389
|
+
})
|
|
1390
|
+
|
|
1391
|
+
test('explicit currency overrides default', () => {
|
|
1392
|
+
const server = session({
|
|
1393
|
+
store: Store.memory(),
|
|
1394
|
+
getClient: () => mockClient,
|
|
1395
|
+
account: '0x0000000000000000000000000000000000000001',
|
|
1396
|
+
currency: '0xcustom',
|
|
1397
|
+
escrowContract: '0x0000000000000000000000000000000000000002',
|
|
1398
|
+
chainId: 4217,
|
|
1399
|
+
testnet: false,
|
|
1400
|
+
} as session.Parameters)
|
|
1401
|
+
expect(server.defaults?.currency).toBe('0xcustom')
|
|
1402
|
+
})
|
|
1403
|
+
|
|
1404
|
+
test('decimals defaults to 6', () => {
|
|
1405
|
+
const server = session({
|
|
1406
|
+
store: Store.memory(),
|
|
1407
|
+
getClient: () => mockClient,
|
|
1408
|
+
account: '0x0000000000000000000000000000000000000001',
|
|
1409
|
+
escrowContract: '0x0000000000000000000000000000000000000002',
|
|
1410
|
+
chainId: 42431,
|
|
1411
|
+
} as session.Parameters)
|
|
1412
|
+
expect(server.defaults?.decimals).toBe(6)
|
|
1413
|
+
})
|
|
1414
|
+
|
|
1415
|
+
test('challenge contains USDC currency (mainnet default)', async () => {
|
|
1416
|
+
const handler = Mppx_server.create({
|
|
1417
|
+
methods: [
|
|
1418
|
+
tempo_server.session({
|
|
1419
|
+
store: Store.memory(),
|
|
1420
|
+
getClient: () => mockMainnetClient,
|
|
1421
|
+
account: '0x0000000000000000000000000000000000000001',
|
|
1422
|
+
escrowContract: '0x0000000000000000000000000000000000000002',
|
|
1423
|
+
chainId: 4217,
|
|
1424
|
+
testnet: false,
|
|
1425
|
+
}),
|
|
1426
|
+
],
|
|
1427
|
+
realm: 'api.example.com',
|
|
1428
|
+
secretKey: 'secret',
|
|
1429
|
+
})
|
|
1430
|
+
|
|
1431
|
+
const result = await (handler.session as Function)({
|
|
1432
|
+
amount: '1',
|
|
1433
|
+
decimals: 6,
|
|
1434
|
+
unitType: 'token',
|
|
1435
|
+
})(new Request('https://example.com'))
|
|
1436
|
+
expect(result.status).toBe(402)
|
|
1437
|
+
|
|
1438
|
+
const challenge = Challenge.fromResponse(result.challenge)
|
|
1439
|
+
expect(challenge.request.currency).toBe('0x20C000000000000000000000b9537d11c60E8b50')
|
|
1440
|
+
})
|
|
1441
|
+
|
|
1442
|
+
test('challenge contains pathUSD currency when testnet: true', async () => {
|
|
1443
|
+
const handler = Mppx_server.create({
|
|
1444
|
+
methods: [
|
|
1445
|
+
tempo_server.session({
|
|
1446
|
+
store: Store.memory(),
|
|
1447
|
+
getClient: () => mockTestnetClient,
|
|
1448
|
+
account: '0x0000000000000000000000000000000000000001',
|
|
1449
|
+
escrowContract: '0x0000000000000000000000000000000000000002',
|
|
1450
|
+
testnet: true,
|
|
1451
|
+
}),
|
|
1452
|
+
],
|
|
1453
|
+
realm: 'api.example.com',
|
|
1454
|
+
secretKey: 'secret',
|
|
1455
|
+
})
|
|
1456
|
+
|
|
1457
|
+
const result = await (handler.session as Function)({
|
|
1458
|
+
amount: '1',
|
|
1459
|
+
decimals: 6,
|
|
1460
|
+
unitType: 'token',
|
|
1461
|
+
chainId: 42431,
|
|
1462
|
+
})(new Request('https://example.com'))
|
|
1463
|
+
expect(result.status).toBe(402)
|
|
1464
|
+
|
|
1465
|
+
const challenge = Challenge.fromResponse(result.challenge)
|
|
1466
|
+
expect(challenge.request.currency).toBe('0x20c0000000000000000000000000000000000000')
|
|
1467
|
+
})
|
|
1468
|
+
|
|
1469
|
+
test('challenge contains pathUSD currency (unknown chain)', async () => {
|
|
1470
|
+
const handler = Mppx_server.create({
|
|
1471
|
+
methods: [
|
|
1472
|
+
tempo_server.session({
|
|
1473
|
+
store: Store.memory(),
|
|
1474
|
+
getClient: () => mockTestnetClient,
|
|
1475
|
+
account: '0x0000000000000000000000000000000000000001',
|
|
1476
|
+
escrowContract: '0x0000000000000000000000000000000000000002',
|
|
1477
|
+
chainId: 69420,
|
|
1478
|
+
}),
|
|
1479
|
+
],
|
|
1480
|
+
realm: 'api.example.com',
|
|
1481
|
+
secretKey: 'secret',
|
|
1482
|
+
})
|
|
1483
|
+
|
|
1484
|
+
const result = await (handler.session as Function)({
|
|
1485
|
+
amount: '1',
|
|
1486
|
+
decimals: 6,
|
|
1487
|
+
unitType: 'token',
|
|
1488
|
+
})(new Request('https://example.com'))
|
|
1489
|
+
expect(result.status).toBe(402)
|
|
1490
|
+
|
|
1491
|
+
const challenge = Challenge.fromResponse(result.challenge)
|
|
1492
|
+
expect(challenge.request.currency).toBe('0x20c0000000000000000000000000000000000000')
|
|
1493
|
+
})
|
|
1494
|
+
|
|
1495
|
+
test('explicit currency in challenge overrides testnet default', async () => {
|
|
1496
|
+
const handler = Mppx_server.create({
|
|
1497
|
+
methods: [
|
|
1498
|
+
tempo_server.session({
|
|
1499
|
+
store: Store.memory(),
|
|
1500
|
+
getClient: () => mockClient,
|
|
1501
|
+
account: '0x0000000000000000000000000000000000000001',
|
|
1502
|
+
currency: '0xcustom',
|
|
1503
|
+
escrowContract: '0x0000000000000000000000000000000000000002',
|
|
1504
|
+
chainId: 4217,
|
|
1505
|
+
testnet: false,
|
|
1506
|
+
}),
|
|
1507
|
+
],
|
|
1508
|
+
realm: 'api.example.com',
|
|
1509
|
+
secretKey: 'secret',
|
|
1510
|
+
})
|
|
1511
|
+
|
|
1512
|
+
const result = await handler.session({
|
|
1513
|
+
amount: '1',
|
|
1514
|
+
decimals: 6,
|
|
1515
|
+
unitType: 'token',
|
|
1516
|
+
})(new Request('https://example.com'))
|
|
1517
|
+
expect(result.status).toBe(402)
|
|
1518
|
+
if (result.status !== 402) throw new Error()
|
|
1519
|
+
|
|
1520
|
+
const challenge = Challenge.fromResponse(result.challenge)
|
|
1521
|
+
expect(challenge.request.currency).toBe('0xcustom')
|
|
1522
|
+
})
|
|
1523
|
+
})
|
|
1524
|
+
|
|
1337
1525
|
function nextSalt(): Hex {
|
|
1338
1526
|
saltCounter++
|
|
1339
1527
|
return `0x${saltCounter.toString(16).padStart(64, '0')}` as Hex
|
|
@@ -85,7 +85,7 @@ export function session<const parameters extends session.Parameters>(p?: paramet
|
|
|
85
85
|
const parameters = p as parameters
|
|
86
86
|
const {
|
|
87
87
|
amount,
|
|
88
|
-
currency,
|
|
88
|
+
currency = defaults.resolveCurrency(parameters),
|
|
89
89
|
decimals = defaults.decimals,
|
|
90
90
|
store: rawStore = Store.memory(),
|
|
91
91
|
suggestedDeposit,
|
|
@@ -127,7 +127,7 @@ export function session<const parameters extends session.Parameters>(p?: paramet
|
|
|
127
127
|
// Extract chainId from request or default.
|
|
128
128
|
const chainId = await (async () => {
|
|
129
129
|
if (request.chainId) return request.chainId
|
|
130
|
-
if (parameters.testnet) return defaults.
|
|
130
|
+
if (parameters.testnet) return defaults.chainId.testnet
|
|
131
131
|
return (await getClient({})).chain?.id
|
|
132
132
|
})()
|
|
133
133
|
|
|
@@ -156,7 +156,12 @@ export function session<const parameters extends session.Parameters>(p?: paramet
|
|
|
156
156
|
return undefined
|
|
157
157
|
})()
|
|
158
158
|
|
|
159
|
-
return {
|
|
159
|
+
return {
|
|
160
|
+
...request,
|
|
161
|
+
chainId,
|
|
162
|
+
escrowContract: resolvedEscrow,
|
|
163
|
+
feePayer: resolvedFeePayer,
|
|
164
|
+
}
|
|
160
165
|
},
|
|
161
166
|
|
|
162
167
|
async verify({ credential }) {
|
|
@@ -131,4 +131,50 @@ describe('Voucher', () => {
|
|
|
131
131
|
expect(voucher.cumulativeAmount).toBe(5000000n)
|
|
132
132
|
expect(voucher.signature).toBe(sig)
|
|
133
133
|
})
|
|
134
|
+
|
|
135
|
+
test('parseVoucherFromPayload with zero amount', () => {
|
|
136
|
+
const sig =
|
|
137
|
+
'0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890ab' as const
|
|
138
|
+
const voucher = parseVoucherFromPayload(channelId, '0', sig)
|
|
139
|
+
expect(voucher.cumulativeAmount).toBe(0n)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
test('verifyVoucher rejects wrong escrow contract', async () => {
|
|
143
|
+
const signature = await signVoucher(
|
|
144
|
+
client,
|
|
145
|
+
account,
|
|
146
|
+
{ channelId, cumulativeAmount },
|
|
147
|
+
escrowContract,
|
|
148
|
+
chainId,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
const wrongEscrow = '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd' as const
|
|
152
|
+
const isValid = await verifyVoucher(
|
|
153
|
+
wrongEscrow,
|
|
154
|
+
chainId,
|
|
155
|
+
{ channelId, cumulativeAmount, signature },
|
|
156
|
+
account.address,
|
|
157
|
+
)
|
|
158
|
+
expect(isValid).toBe(false)
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
test('signVoucher and verifyVoucher round-trip with zero amount', async () => {
|
|
162
|
+
const zeroAmount = 0n
|
|
163
|
+
const signature = await signVoucher(
|
|
164
|
+
client,
|
|
165
|
+
account,
|
|
166
|
+
{ channelId, cumulativeAmount: zeroAmount },
|
|
167
|
+
escrowContract,
|
|
168
|
+
chainId,
|
|
169
|
+
)
|
|
170
|
+
expect(signature).toMatch(/^0x/)
|
|
171
|
+
|
|
172
|
+
const isValid = await verifyVoucher(
|
|
173
|
+
escrowContract,
|
|
174
|
+
chainId,
|
|
175
|
+
{ channelId, cumulativeAmount: zeroAmount, signature },
|
|
176
|
+
account.address,
|
|
177
|
+
)
|
|
178
|
+
expect(isValid).toBe(true)
|
|
179
|
+
})
|
|
134
180
|
})
|