@remitmd/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +250 -0
  3. package/dist/a2a.d.ts +137 -0
  4. package/dist/a2a.d.ts.map +1 -0
  5. package/dist/a2a.js +121 -0
  6. package/dist/a2a.js.map +1 -0
  7. package/dist/client.d.ts +41 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/client.js +81 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/errors.d.ts +108 -0
  12. package/dist/errors.d.ts.map +1 -0
  13. package/dist/errors.js +218 -0
  14. package/dist/errors.js.map +1 -0
  15. package/dist/http.d.ts +23 -0
  16. package/dist/http.d.ts.map +1 -0
  17. package/dist/http.js +150 -0
  18. package/dist/http.js.map +1 -0
  19. package/dist/index.d.ts +18 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +21 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/integrations/vercel-ai.d.ts +44 -0
  24. package/dist/integrations/vercel-ai.d.ts.map +1 -0
  25. package/dist/integrations/vercel-ai.js +175 -0
  26. package/dist/integrations/vercel-ai.js.map +1 -0
  27. package/dist/models/bounty.d.ts +22 -0
  28. package/dist/models/bounty.d.ts.map +1 -0
  29. package/dist/models/bounty.js +2 -0
  30. package/dist/models/bounty.js.map +1 -0
  31. package/dist/models/common.d.ts +78 -0
  32. package/dist/models/common.d.ts.map +1 -0
  33. package/dist/models/common.js +3 -0
  34. package/dist/models/common.js.map +1 -0
  35. package/dist/models/deposit.d.ts +13 -0
  36. package/dist/models/deposit.d.ts.map +1 -0
  37. package/dist/models/deposit.js +2 -0
  38. package/dist/models/deposit.js.map +1 -0
  39. package/dist/models/escrow.d.ts +16 -0
  40. package/dist/models/escrow.d.ts.map +1 -0
  41. package/dist/models/escrow.js +2 -0
  42. package/dist/models/escrow.js.map +1 -0
  43. package/dist/models/index.d.ts +9 -0
  44. package/dist/models/index.d.ts.map +1 -0
  45. package/dist/models/index.js +9 -0
  46. package/dist/models/index.js.map +1 -0
  47. package/dist/models/invoice.d.ts +30 -0
  48. package/dist/models/invoice.d.ts.map +1 -0
  49. package/dist/models/invoice.js +2 -0
  50. package/dist/models/invoice.js.map +1 -0
  51. package/dist/models/reputation.d.ts +7 -0
  52. package/dist/models/reputation.d.ts.map +1 -0
  53. package/dist/models/reputation.js +2 -0
  54. package/dist/models/reputation.js.map +1 -0
  55. package/dist/models/stream.d.ts +15 -0
  56. package/dist/models/stream.d.ts.map +1 -0
  57. package/dist/models/stream.js +2 -0
  58. package/dist/models/stream.js.map +1 -0
  59. package/dist/models/tab.d.ts +21 -0
  60. package/dist/models/tab.d.ts.map +1 -0
  61. package/dist/models/tab.js +2 -0
  62. package/dist/models/tab.js.map +1 -0
  63. package/dist/provider.d.ts +135 -0
  64. package/dist/provider.d.ts.map +1 -0
  65. package/dist/provider.js +218 -0
  66. package/dist/provider.js.map +1 -0
  67. package/dist/signer.d.ts +31 -0
  68. package/dist/signer.d.ts.map +1 -0
  69. package/dist/signer.js +35 -0
  70. package/dist/signer.js.map +1 -0
  71. package/dist/testing/local.d.ts +31 -0
  72. package/dist/testing/local.d.ts.map +1 -0
  73. package/dist/testing/local.js +100 -0
  74. package/dist/testing/local.js.map +1 -0
  75. package/dist/testing/mock.d.ts +95 -0
  76. package/dist/testing/mock.d.ts.map +1 -0
  77. package/dist/testing/mock.js +407 -0
  78. package/dist/testing/mock.js.map +1 -0
  79. package/dist/wallet.d.ts +162 -0
  80. package/dist/wallet.d.ts.map +1 -0
  81. package/dist/wallet.js +365 -0
  82. package/dist/wallet.js.map +1 -0
  83. package/dist/x402.d.ts +78 -0
  84. package/dist/x402.d.ts.map +1 -0
  85. package/dist/x402.js +151 -0
  86. package/dist/x402.js.map +1 -0
  87. package/eslint.config.js +27 -0
  88. package/package.json +39 -0
  89. package/src/a2a.ts +241 -0
  90. package/src/client.ts +104 -0
  91. package/src/errors.ts +261 -0
  92. package/src/http.ts +190 -0
  93. package/src/index.ts +94 -0
  94. package/src/integrations/vercel-ai.ts +213 -0
  95. package/src/models/bounty.ts +23 -0
  96. package/src/models/common.ts +106 -0
  97. package/src/models/deposit.ts +13 -0
  98. package/src/models/escrow.ts +16 -0
  99. package/src/models/index.ts +8 -0
  100. package/src/models/invoice.ts +32 -0
  101. package/src/models/reputation.ts +7 -0
  102. package/src/models/stream.ts +15 -0
  103. package/src/models/tab.ts +22 -0
  104. package/src/provider.ts +281 -0
  105. package/src/signer.ts +70 -0
  106. package/src/testing/local.ts +118 -0
  107. package/src/testing/mock.ts +507 -0
  108. package/src/wallet.ts +546 -0
  109. package/src/x402.ts +202 -0
  110. package/tests/acceptance/bounty.test.ts +82 -0
  111. package/tests/acceptance/deposit.test.ts +70 -0
  112. package/tests/acceptance/direct.test.ts +53 -0
  113. package/tests/acceptance/escrow.test.ts +67 -0
  114. package/tests/acceptance/setup.ts +113 -0
  115. package/tests/acceptance/stream.test.ts +98 -0
  116. package/tests/acceptance/tab.test.ts +108 -0
  117. package/tests/acceptance/x402.test.ts +140 -0
  118. package/tests/compliance/auth.ts +69 -0
  119. package/tests/compliance/escrows.ts +96 -0
  120. package/tests/compliance/helpers.ts +90 -0
  121. package/tests/compliance/payments.ts +69 -0
  122. package/tests/compliance/tabs.ts +52 -0
  123. package/tests/test_a2a.ts +151 -0
  124. package/tests/test_errors.ts +80 -0
  125. package/tests/test_golden_vectors.ts +162 -0
  126. package/tests/test_integrations.ts +115 -0
  127. package/tests/test_mock.ts +217 -0
  128. package/tests/test_permit.ts +216 -0
  129. package/tests/test_provider.ts +304 -0
  130. package/tests/test_wallet.ts +108 -0
  131. package/tests/test_x402.ts +302 -0
  132. package/tsconfig.json +19 -0
@@ -0,0 +1,217 @@
1
+ import { describe, it, beforeEach } from "node:test";
2
+ import assert from "node:assert/strict";
3
+
4
+ import { MockRemit } from "../src/testing/mock.js";
5
+ import { InsufficientBalanceError } from "../src/errors.js";
6
+
7
+ describe("MockRemit", () => {
8
+ let mock: MockRemit;
9
+
10
+ beforeEach(() => {
11
+ mock = new MockRemit();
12
+ });
13
+
14
+ it("createWallet returns wallet with correct balance", async () => {
15
+ const wallet = mock.createWallet(500);
16
+ const bal = await wallet.balance();
17
+ assert.equal(bal, 500);
18
+ });
19
+
20
+ it("payDirect transfers funds", async () => {
21
+ const payer = mock.createWallet(100);
22
+ const payee = mock.createWallet(0);
23
+ const tx = await payer.payDirect(payee.address, 30);
24
+ assert.equal(tx.status, "confirmed");
25
+ assert.equal(await payer.balance(), 70);
26
+ assert.equal(await payee.balance(), 30);
27
+ });
28
+
29
+ it("payDirect throws on insufficient balance", async () => {
30
+ const payer = mock.createWallet(10);
31
+ const payee = mock.createWallet(0);
32
+ await assert.rejects(
33
+ () => payer.payDirect(payee.address, 100),
34
+ InsufficientBalanceError,
35
+ );
36
+ });
37
+
38
+ it("escrow: fund, release", async () => {
39
+ const payer = mock.createWallet(200);
40
+ const payee = mock.createWallet(0);
41
+
42
+ const tx = await payer.pay({
43
+ id: "inv-001",
44
+ from: payer.address,
45
+ to: payee.address,
46
+ amount: 100,
47
+ chain: "base",
48
+ status: "pending",
49
+ paymentType: "escrow",
50
+ createdAt: Math.floor(Date.now() / 1000),
51
+ });
52
+
53
+ assert(tx.txHash);
54
+ assert.equal(await payer.balance(), 100); // deducted on fund
55
+
56
+ const releaseTx = await payer.releaseEscrow("inv-001");
57
+ assert.equal(releaseTx.status, "confirmed");
58
+ assert.equal(await payee.balance(), 100);
59
+ });
60
+
61
+ it("escrow: fund, cancel — refunds payer", async () => {
62
+ const payer = mock.createWallet(200);
63
+ const payee = mock.createWallet(0);
64
+
65
+ await payer.pay({
66
+ id: "inv-002",
67
+ from: payer.address,
68
+ to: payee.address,
69
+ amount: 50,
70
+ chain: "base",
71
+ status: "pending",
72
+ paymentType: "escrow",
73
+ createdAt: Math.floor(Date.now() / 1000),
74
+ });
75
+
76
+ assert.equal(await payer.balance(), 150);
77
+ await payer.cancelEscrow("inv-002");
78
+ assert.equal(await payer.balance(), 200); // refunded
79
+ assert.equal(await payee.balance(), 0);
80
+ });
81
+
82
+ it("tab: open, close — refunds unspent", async () => {
83
+ const payer = mock.createWallet(100);
84
+ const payee = mock.createWallet(0);
85
+
86
+ const tab = await payer.openTab({ to: payee.address, limit: 50, perUnit: 5 });
87
+ assert.equal(tab.status, "open");
88
+ assert.equal(await payer.balance(), 50); // limit deducted
89
+
90
+ const closeTx = await payer.closeTab(tab.id);
91
+ assert.equal(closeTx.status, "confirmed");
92
+ // No charges — full refund
93
+ assert.equal(await payer.balance(), 100);
94
+ assert.equal(await payee.balance(), 0);
95
+ });
96
+
97
+ it("tab throws when tab not found", async () => {
98
+ const payer = mock.createWallet(100);
99
+ await assert.rejects(() => payer.closeTab("nonexistent"), /TAB_NOT_FOUND|Tab not found/);
100
+ });
101
+
102
+ it("stream: open, close after time advance — correct accounting", async () => {
103
+ const payer = mock.createWallet(1000);
104
+ const payee = mock.createWallet(0);
105
+
106
+ const stream = await payer.openStream({ to: payee.address, rate: 1, maxDuration: 100 });
107
+ assert.equal(stream.status, "active");
108
+ assert.equal(await payer.balance(), 900); // reserve of 100 deducted
109
+
110
+ mock.advanceTime(30);
111
+ await payer.closeStream(stream.id);
112
+
113
+ const payerBal = await payer.balance();
114
+ const payeeBal = await payee.balance();
115
+ assert(payerBal > 960 && payerBal <= 970, `payer balance ${payerBal} should be ~970`);
116
+ assert(payeeBal >= 30 && payeeBal <= 40, `payee balance ${payeeBal} should be ~30`);
117
+ });
118
+
119
+ it("bounty: post, award", async () => {
120
+ const poster = mock.createWallet(100);
121
+ const winner = mock.createWallet(0);
122
+ const deadline = Math.floor(Date.now() / 1000) + 86400;
123
+
124
+ const bounty = await poster.postBounty({ amount: 50, task: "Write tests", deadline });
125
+ assert.equal(bounty.status, "open");
126
+ assert.equal(await poster.balance(), 50);
127
+
128
+ await winner.submitBounty(bounty.id, "0xevidence");
129
+ await poster.awardBounty(bounty.id, 1);
130
+ const b = mock.getBounty(bounty.id);
131
+ assert.equal(b.status, "awarded");
132
+ assert.equal(b.winner, winner.address);
133
+ assert.equal(await winner.balance(), 50);
134
+ });
135
+
136
+ it("deposit: locks funds", async () => {
137
+ const payer = mock.createWallet(100);
138
+ const payee = mock.createWallet(0);
139
+
140
+ const deposit = await payer.placeDeposit({ to: payee.address, amount: 30, expires: 3600 });
141
+ assert.equal(deposit.status, "locked");
142
+ assert.equal(await payer.balance(), 70);
143
+ });
144
+
145
+ it("status includes balance and tier", async () => {
146
+ const wallet = mock.createWallet(555);
147
+ const status = await wallet.status();
148
+ assert.equal(parseFloat(status.balance), 555);
149
+ assert(status.tier.length > 0);
150
+ });
151
+
152
+ it("reputation returns valid profile", async () => {
153
+ const wallet = mock.createWallet();
154
+ const rep = await wallet.getReputation(wallet.address);
155
+ assert.equal(rep.address, wallet.address);
156
+ assert(rep.score >= 0);
157
+ });
158
+
159
+ it("setBehavior forces next operation to fail", async () => {
160
+ const payer = mock.createWallet(100);
161
+ const payee = mock.createWallet(0);
162
+ mock.setBehavior(payer.address, "INSUFFICIENT_BALANCE");
163
+ await assert.rejects(() => payer.payDirect(payee.address, 1));
164
+ // Second call should succeed (error was consumed)
165
+ const tx = await payer.payDirect(payee.address, 1);
166
+ assert.equal(tx.status, "confirmed");
167
+ });
168
+
169
+ it("advanceTime affects stream accounting", async () => {
170
+ const payer = mock.createWallet(1000);
171
+ const payee = mock.createWallet(0);
172
+
173
+ const stream = await payer.openStream({ to: payee.address, rate: 10, maxDuration: 60 });
174
+ mock.advanceTime(60); // full duration
175
+ await payer.closeStream(stream.id);
176
+
177
+ assert.equal(await payee.balance(), 600); // 10/s * 60s
178
+ assert.equal(await payer.balance(), 400); // 1000 - 600
179
+ });
180
+
181
+ it("listBounties filters by status", async () => {
182
+ const poster = mock.createWallet(1000);
183
+ const w2 = mock.createWallet(0);
184
+ const deadline = Math.floor(Date.now() / 1000) + 86400;
185
+
186
+ const b1 = await poster.postBounty({ amount: 10, task: "Task A", deadline });
187
+ await poster.postBounty({ amount: 10, task: "Task B", deadline });
188
+ await w2.submitBounty(b1.id, "0xevidence");
189
+ await poster.awardBounty(b1.id, 1);
190
+
191
+ const open = mock.listBounties("open");
192
+ const awarded = mock.listBounties("awarded");
193
+ assert.equal(open.length, 1);
194
+ assert.equal(awarded.length, 1);
195
+ });
196
+
197
+ it("requestTestnetFunds adds 100 USDC", async () => {
198
+ const wallet = mock.createWallet(0);
199
+ await wallet.requestTestnetFunds();
200
+ assert.equal(await wallet.balance(), 100);
201
+ });
202
+
203
+ it("registerWebhook returns webhook object", async () => {
204
+ const wallet = mock.createWallet();
205
+ const wh = await wallet.registerWebhook("https://example.com/hook", ["payment.completed"]);
206
+ assert.equal(wh.url, "https://example.com/hook");
207
+ assert(wh.id.length > 0);
208
+ });
209
+
210
+ it("wallet toJSON does not expose private key", () => {
211
+ const wallet = mock.createWallet();
212
+ const json = JSON.stringify(wallet);
213
+ // Should not contain anything resembling a private key
214
+ assert(!json.includes("0x") || json.includes(wallet.address));
215
+ assert(json.includes(wallet.address));
216
+ });
217
+ });
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Tests for EIP-2612 permit signing (signUsdcPermit / signPermit).
3
+ *
4
+ * Verifies:
5
+ * - signUsdcPermit produces valid v/r/s format
6
+ * - Domain matches MockUSDC (name="USD Coin", version="2")
7
+ * - Signature is recoverable to the signer's address
8
+ * - signPermit computes correct raw amounts from USDC decimals
9
+ */
10
+
11
+ import { describe, it } from "node:test";
12
+ import assert from "node:assert/strict";
13
+ import { hashTypedData, recoverTypedDataAddress } from "viem";
14
+
15
+ import { Wallet } from "../src/wallet.js";
16
+
17
+ // Anvil test wallet #0
18
+ const TEST_KEY = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
19
+ const TEST_ADDR = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
20
+
21
+ // Known contract addresses (Base Sepolia)
22
+ const USDC_ADDRESS = "0x142aD61B8d2edD6b3807D9266866D97C35Ee0317";
23
+ const ROUTER_ADDRESS = "0xb3E96ebE54138d1c0caea00Ae098309C7E0138eC";
24
+ const ESCROW_ADDRESS = "0x9AC531dd432d5dcF637D288290E5A23F2eE36594";
25
+
26
+ // EIP-2612 permit types
27
+ const PERMIT_TYPES = {
28
+ Permit: [
29
+ { name: "owner", type: "address" },
30
+ { name: "spender", type: "address" },
31
+ { name: "value", type: "uint256" },
32
+ { name: "nonce", type: "uint256" },
33
+ { name: "deadline", type: "uint256" },
34
+ ],
35
+ } as const;
36
+
37
+ const USDC_DOMAIN = {
38
+ name: "USD Coin",
39
+ version: "2",
40
+ chainId: 84532,
41
+ verifyingContract: USDC_ADDRESS as `0x${string}`,
42
+ };
43
+
44
+ describe("signUsdcPermit", () => {
45
+ const wallet = new Wallet({
46
+ privateKey: TEST_KEY,
47
+ chain: "base-sepolia",
48
+ });
49
+
50
+ it("returns valid PermitSignature shape", async () => {
51
+ const permit = await wallet.signUsdcPermit({
52
+ spender: ROUTER_ADDRESS,
53
+ value: BigInt(1_000_000), // 1 USDC
54
+ deadline: 1999999999,
55
+ nonce: 0,
56
+ });
57
+
58
+ assert.equal(typeof permit.value, "number");
59
+ assert.equal(permit.value, 1_000_000);
60
+ assert.equal(permit.deadline, 1999999999);
61
+ assert.equal(typeof permit.v, "number");
62
+ assert(permit.v === 27 || permit.v === 28, `v must be 27 or 28, got ${permit.v}`);
63
+ assert.match(permit.r, /^0x[0-9a-f]{64}$/i, "r must be 32 bytes hex");
64
+ assert.match(permit.s, /^0x[0-9a-f]{64}$/i, "s must be 32 bytes hex");
65
+ });
66
+
67
+ it("signature recovers to the wallet address", async () => {
68
+ const spender = ESCROW_ADDRESS;
69
+ const value = BigInt(5_000_000); // 5 USDC
70
+ const deadline = 2000000000;
71
+ const nonce = 0;
72
+
73
+ const permit = await wallet.signUsdcPermit({
74
+ spender,
75
+ value,
76
+ deadline,
77
+ nonce,
78
+ });
79
+
80
+ // Reconstruct the full signature hex
81
+ const rHex = permit.r.slice(2);
82
+ const sHex = permit.s.slice(2);
83
+ const vHex = permit.v.toString(16).padStart(2, "0");
84
+ const fullSig = `0x${rHex}${sHex}${vHex}` as `0x${string}`;
85
+
86
+ const recovered = await recoverTypedDataAddress({
87
+ domain: USDC_DOMAIN,
88
+ types: PERMIT_TYPES,
89
+ primaryType: "Permit",
90
+ message: {
91
+ owner: TEST_ADDR as `0x${string}`,
92
+ spender: spender as `0x${string}`,
93
+ value,
94
+ nonce: BigInt(nonce),
95
+ deadline: BigInt(deadline),
96
+ },
97
+ signature: fullSig,
98
+ });
99
+
100
+ assert.equal(
101
+ recovered.toLowerCase(),
102
+ TEST_ADDR.toLowerCase(),
103
+ "recovered address must match signer",
104
+ );
105
+ });
106
+
107
+ it("different nonces produce different signatures", async () => {
108
+ const base = {
109
+ spender: ROUTER_ADDRESS,
110
+ value: BigInt(1_000_000),
111
+ deadline: 1999999999,
112
+ };
113
+
114
+ const sig0 = await wallet.signUsdcPermit({ ...base, nonce: 0 });
115
+ const sig1 = await wallet.signUsdcPermit({ ...base, nonce: 1 });
116
+
117
+ assert.notEqual(sig0.r, sig1.r, "different nonces must produce different r");
118
+ });
119
+
120
+ it("different spenders produce different signatures", async () => {
121
+ const base = {
122
+ value: BigInt(1_000_000),
123
+ deadline: 1999999999,
124
+ nonce: 0,
125
+ };
126
+
127
+ const sigRouter = await wallet.signUsdcPermit({ ...base, spender: ROUTER_ADDRESS });
128
+ const sigEscrow = await wallet.signUsdcPermit({ ...base, spender: ESCROW_ADDRESS });
129
+
130
+ assert.notEqual(sigRouter.r, sigEscrow.r, "different spenders must produce different r");
131
+ });
132
+
133
+ it("uses correct EIP-712 domain (USD Coin v2)", async () => {
134
+ // Hash the typed data ourselves and verify the wallet's signature matches
135
+ const spender = ROUTER_ADDRESS;
136
+ const value = BigInt(2_500_000); // 2.5 USDC
137
+ const deadline = 2000000000;
138
+ const nonce = 0;
139
+
140
+ const expectedHash = hashTypedData({
141
+ domain: USDC_DOMAIN,
142
+ types: PERMIT_TYPES,
143
+ primaryType: "Permit",
144
+ message: {
145
+ owner: TEST_ADDR as `0x${string}`,
146
+ spender: spender as `0x${string}`,
147
+ value,
148
+ nonce: BigInt(nonce),
149
+ deadline: BigInt(deadline),
150
+ },
151
+ });
152
+
153
+ // Just verify the hash is deterministic — domain is correct if recovery works
154
+ assert.match(expectedHash, /^0x[0-9a-f]{64}$/i);
155
+
156
+ const permit = await wallet.signUsdcPermit({
157
+ spender,
158
+ value,
159
+ deadline,
160
+ nonce,
161
+ });
162
+
163
+ // Verify recovery with the domain we used
164
+ const rHex = permit.r.slice(2);
165
+ const sHex = permit.s.slice(2);
166
+ const vHex = permit.v.toString(16).padStart(2, "0");
167
+ const fullSig = `0x${rHex}${sHex}${vHex}` as `0x${string}`;
168
+
169
+ const recovered = await recoverTypedDataAddress({
170
+ domain: USDC_DOMAIN,
171
+ types: PERMIT_TYPES,
172
+ primaryType: "Permit",
173
+ message: {
174
+ owner: TEST_ADDR as `0x${string}`,
175
+ spender: spender as `0x${string}`,
176
+ value,
177
+ nonce: BigInt(nonce),
178
+ deadline: BigInt(deadline),
179
+ },
180
+ signature: fullSig,
181
+ });
182
+
183
+ assert.equal(
184
+ recovered.toLowerCase(),
185
+ TEST_ADDR.toLowerCase(),
186
+ "must recover with USD Coin v2 domain",
187
+ );
188
+ });
189
+ });
190
+
191
+ describe("PermitSignature value ranges", () => {
192
+ const wallet = new Wallet({
193
+ privateKey: TEST_KEY,
194
+ chain: "base-sepolia",
195
+ });
196
+
197
+ it("handles zero value", async () => {
198
+ const permit = await wallet.signUsdcPermit({
199
+ spender: ROUTER_ADDRESS,
200
+ value: BigInt(0),
201
+ deadline: 1999999999,
202
+ nonce: 0,
203
+ });
204
+ assert.equal(permit.value, 0);
205
+ });
206
+
207
+ it("handles large value ($2,500 = 2_500_000_000 raw)", async () => {
208
+ const permit = await wallet.signUsdcPermit({
209
+ spender: ROUTER_ADDRESS,
210
+ value: BigInt(2_500_000_000),
211
+ deadline: 1999999999,
212
+ nonce: 0,
213
+ });
214
+ assert.equal(permit.value, 2_500_000_000);
215
+ });
216
+ });