moltlaunch 2.0.0 → 2.0.2

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 (108) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +18 -18
  3. package/dist/index.js.map +1 -1
  4. package/package.json +6 -2
  5. package/.claude/commands/deploy.md +0 -33
  6. package/.claude/hooks/regenerate-docs.sh +0 -12
  7. package/.claude/settings.json +0 -15
  8. package/.env.example +0 -2
  9. package/.github/workflows/deploy.yml +0 -37
  10. package/ROADMAP.md +0 -29
  11. package/contracts/MandateEscrowV4.sol +0 -281
  12. package/contracts/mocks/MockFlaunchBuyback.sol +0 -24
  13. package/hardhat.config.cjs +0 -29
  14. package/scripts/check-deploy-cost.ts +0 -15
  15. package/scripts/deploy-escrow-v4.ts +0 -81
  16. package/scripts/deploy-escrow.cjs +0 -22
  17. package/scripts/generate-docs.ts +0 -309
  18. package/shared/manifest.json +0 -87
  19. package/site/.vscode/extensions.json +0 -4
  20. package/site/.vscode/launch.json +0 -11
  21. package/site/README.md +0 -43
  22. package/site/astro.config.mjs +0 -21
  23. package/site/functions/agent/[[path]].ts +0 -9
  24. package/site/functions/task/[[path]].ts +0 -9
  25. package/site/index.html.bak +0 -1755
  26. package/site/package-lock.json +0 -6165
  27. package/site/package.json +0 -17
  28. package/site/public/_redirects +0 -1
  29. package/site/public/art/hero.webp +0 -0
  30. package/site/public/favicon.ico +0 -0
  31. package/site/public/favicon.svg +0 -4
  32. package/site/public/logo.png +0 -0
  33. package/site/public/skill.md +0 -276
  34. package/site/src/components/AgentGridCard.astro +0 -97
  35. package/site/src/components/AgentRow.astro +0 -75
  36. package/site/src/components/Footer.astro +0 -71
  37. package/site/src/components/GigCard.astro +0 -36
  38. package/site/src/components/Navbar.astro +0 -93
  39. package/site/src/components/ReviewCard.astro +0 -29
  40. package/site/src/components/SkillPill.astro +0 -19
  41. package/site/src/components/StatusBadge.astro +0 -27
  42. package/site/src/components/TaskEntry.astro +0 -98
  43. package/site/src/layouts/Layout.astro +0 -268
  44. package/site/src/lib/api.ts +0 -342
  45. package/site/src/pages/404.astro +0 -33
  46. package/site/src/pages/admin.astro +0 -445
  47. package/site/src/pages/agent/[...id].astro +0 -678
  48. package/site/src/pages/agents/index.astro +0 -235
  49. package/site/src/pages/dashboard.astro +0 -244
  50. package/site/src/pages/docs.astro +0 -191
  51. package/site/src/pages/how.astro +0 -156
  52. package/site/src/pages/index.astro +0 -226
  53. package/site/src/pages/leaderboard.astro +0 -155
  54. package/site/src/pages/task/[...id].astro +0 -1467
  55. package/site/src/styles/global.css +0 -159
  56. package/site/tailwind.config.mjs +0 -94
  57. package/site/tsconfig.json +0 -5
  58. package/site/wrangler.toml +0 -5
  59. package/src/commands/accept.ts +0 -135
  60. package/src/commands/agents.ts +0 -190
  61. package/src/commands/approve.ts +0 -127
  62. package/src/commands/claim.ts +0 -130
  63. package/src/commands/decline.ts +0 -55
  64. package/src/commands/dispute.ts +0 -92
  65. package/src/commands/earnings.ts +0 -86
  66. package/src/commands/feedback.ts +0 -147
  67. package/src/commands/gig.ts +0 -141
  68. package/src/commands/hire.ts +0 -96
  69. package/src/commands/inbox.ts +0 -135
  70. package/src/commands/message.ts +0 -97
  71. package/src/commands/profile.ts +0 -62
  72. package/src/commands/quote.ts +0 -80
  73. package/src/commands/refund.ts +0 -82
  74. package/src/commands/register.ts +0 -250
  75. package/src/commands/resolve.ts +0 -104
  76. package/src/commands/reviews.ts +0 -78
  77. package/src/commands/revise.ts +0 -65
  78. package/src/commands/submit.ts +0 -123
  79. package/src/commands/tasks.ts +0 -224
  80. package/src/commands/view.ts +0 -122
  81. package/src/commands/wallet.ts +0 -42
  82. package/src/index.ts +0 -285
  83. package/src/lib/agent0.ts +0 -158
  84. package/src/lib/auth.ts +0 -25
  85. package/src/lib/constants.ts +0 -55
  86. package/src/lib/escrow.ts +0 -374
  87. package/src/lib/files.ts +0 -87
  88. package/src/lib/flaunch.ts +0 -277
  89. package/src/lib/mandate.ts +0 -623
  90. package/src/lib/tasks.ts +0 -466
  91. package/src/lib/types.ts +0 -112
  92. package/src/lib/wallet.ts +0 -119
  93. package/src/lib/x402.ts +0 -86
  94. package/test/MandateEscrowV4.test.cjs +0 -568
  95. package/tsconfig.json +0 -19
  96. package/tsup.config.ts +0 -15
  97. package/worker/package-lock.json +0 -1812
  98. package/worker/package.json +0 -18
  99. package/worker/src/agents.ts +0 -755
  100. package/worker/src/auth.ts +0 -126
  101. package/worker/src/files.ts +0 -40
  102. package/worker/src/index.ts +0 -963
  103. package/worker/src/profiles.ts +0 -85
  104. package/worker/src/ratelimit.ts +0 -45
  105. package/worker/src/tasks.ts +0 -498
  106. package/worker/src/types.ts +0 -95
  107. package/worker/tsconfig.json +0 -15
  108. package/worker/wrangler.toml +0 -19
package/src/lib/x402.ts DELETED
@@ -1,86 +0,0 @@
1
- // x402: HTTP 402 Payment Protocol Client
2
- // Using official @x402 SDK from Coinbase
3
-
4
- import { wrapFetchWithPayment, x402Client } from "@x402/fetch";
5
- import { ExactEvmScheme, toClientEvmSigner } from "@x402/evm";
6
- import { privateKeyToAccount } from "viem/accounts";
7
- import type { Wallet, HireResult } from "./types.js";
8
-
9
- // Base mainnet network ID (CAIP-2 format)
10
- const BASE_NETWORK = "eip155:8453";
11
-
12
- /**
13
- * Create a payment-enabled fetch function for a wallet
14
- */
15
- export function createPayingFetch(wallet: Wallet) {
16
- // Create viem account (matches ClientEvmSigner interface)
17
- const account = privateKeyToAccount(wallet.privateKey);
18
-
19
- // Create x402 client with EVM scheme for Base
20
- const client = new x402Client()
21
- .register(BASE_NETWORK, new ExactEvmScheme(account));
22
-
23
- // Wrap fetch to automatically handle 402 payments
24
- return wrapFetchWithPayment(fetch, client);
25
- }
26
-
27
- /**
28
- * Hire an agent via x402 protocol using official SDK
29
- *
30
- * Flow:
31
- * 1. POST to agent endpoint with task
32
- * 2. If 402, SDK automatically handles payment
33
- * 3. Returns result after payment settles
34
- */
35
- export async function hireAgent(
36
- wallet: Wallet,
37
- endpoint: string,
38
- task: string,
39
- _maxBudget?: bigint
40
- ): Promise<HireResult> {
41
- try {
42
- // Get payment-enabled fetch
43
- const payingFetch = createPayingFetch(wallet);
44
-
45
- // Make request - SDK handles 402 automatically
46
- const response = await payingFetch(endpoint, {
47
- method: "POST",
48
- headers: {
49
- "Content-Type": "application/json",
50
- },
51
- body: JSON.stringify({ task }),
52
- });
53
-
54
- if (response.ok) {
55
- const result = await response.json();
56
- return {
57
- success: true,
58
- result: result.result,
59
- subcontracts: result.subcontracts,
60
- };
61
- } else {
62
- return {
63
- success: false,
64
- error: `Request failed: ${response.status} ${response.statusText}`,
65
- };
66
- }
67
- } catch (err) {
68
- return {
69
- success: false,
70
- error: `Hire failed: ${err instanceof Error ? err.message : String(err)}`,
71
- };
72
- }
73
- }
74
-
75
- /**
76
- * Expose x402Client for advanced usage
77
- */
78
- export function getX402Client(wallet: Wallet): x402Client {
79
- const account = privateKeyToAccount(wallet.privateKey);
80
- return new x402Client()
81
- .register(BASE_NETWORK, new ExactEvmScheme(account));
82
- }
83
-
84
- // Re-export types for convenience
85
- export { x402Client } from "@x402/fetch";
86
- export { ExactEvmScheme } from "@x402/evm";
@@ -1,568 +0,0 @@
1
- const { expect } = require("chai");
2
- const { ethers } = require("hardhat");
3
- const { time } = require("@nomicfoundation/hardhat-network-helpers");
4
-
5
- describe("MandateEscrowV4", function () {
6
- let escrow, mockBuyback, failingBuyback;
7
- let admin, client, agent, other;
8
- let token;
9
- const TASK_ID = ethers.keccak256(ethers.toUtf8Bytes("test-task-v4-1"));
10
- const TIMEOUT = 24 * 60 * 60; // 24 hours in seconds
11
-
12
- beforeEach(async function () {
13
- [admin, client, agent, other] = await ethers.getSigners();
14
- token = other.address;
15
-
16
- const MockBuyback = await ethers.getContractFactory("MockFlaunchBuyback");
17
- mockBuyback = await MockBuyback.deploy();
18
-
19
- const FailingBuyback = await ethers.getContractFactory("FailingBuyback");
20
- failingBuyback = await FailingBuyback.deploy();
21
-
22
- const Escrow = await ethers.getContractFactory("MandateEscrowV4");
23
- escrow = await Escrow.deploy(await mockBuyback.getAddress());
24
- });
25
-
26
- // ─── Deposit ───────────────────────────────────────────────
27
-
28
- describe("Deposit", function () {
29
- it("should deposit successfully", async function () {
30
- const amount = ethers.parseEther("0.1");
31
- await expect(
32
- escrow.connect(client).deposit(TASK_ID, agent.address, token, { value: amount })
33
- )
34
- .to.emit(escrow, "Deposited")
35
- .withArgs(TASK_ID, client.address, token, amount);
36
-
37
- const e = await escrow.getEscrow(TASK_ID);
38
- expect(e.client).to.equal(client.address);
39
- expect(e.agent).to.equal(agent.address);
40
- expect(e.amount).to.equal(amount);
41
- expect(e.status).to.equal(0); // Active
42
- });
43
-
44
- it("should revert on zero value", async function () {
45
- await expect(
46
- escrow.connect(client).deposit(TASK_ID, agent.address, token, { value: 0 })
47
- ).to.be.revertedWith("No value");
48
- });
49
-
50
- it("should revert on zero-address agent", async function () {
51
- await expect(
52
- escrow.connect(client).deposit(TASK_ID, ethers.ZeroAddress, token, {
53
- value: ethers.parseEther("0.1"),
54
- })
55
- ).to.be.revertedWith("Invalid agent");
56
- });
57
-
58
- it("should revert on zero-address token", async function () {
59
- await expect(
60
- escrow.connect(client).deposit(TASK_ID, agent.address, ethers.ZeroAddress, {
61
- value: ethers.parseEther("0.1"),
62
- })
63
- ).to.be.revertedWith("Invalid token");
64
- });
65
-
66
- it("should revert on duplicate deposit", async function () {
67
- const amount = ethers.parseEther("0.1");
68
- await escrow.connect(client).deposit(TASK_ID, agent.address, token, { value: amount });
69
-
70
- await expect(
71
- escrow.connect(client).deposit(TASK_ID, agent.address, token, { value: amount })
72
- ).to.be.revertedWith("Task exists");
73
- });
74
- });
75
-
76
- // ─── Mark Submitted ────────────────────────────────────────
77
-
78
- describe("Mark Submitted", function () {
79
- beforeEach(async function () {
80
- await escrow.connect(client).deposit(TASK_ID, agent.address, token, {
81
- value: ethers.parseEther("0.1"),
82
- });
83
- });
84
-
85
- it("should mark submitted and emit event", async function () {
86
- const tx = await escrow.connect(agent).markSubmitted(TASK_ID);
87
- const receipt = await tx.wait();
88
- const block = await ethers.provider.getBlock(receipt.blockNumber);
89
-
90
- await expect(tx)
91
- .to.emit(escrow, "Submitted")
92
- .withArgs(TASK_ID, agent.address, block.timestamp + TIMEOUT);
93
-
94
- expect(await escrow.getStatus(TASK_ID)).to.equal(1); // Submitted
95
- });
96
-
97
- it("should revert if caller is not agent", async function () {
98
- await expect(
99
- escrow.connect(other).markSubmitted(TASK_ID)
100
- ).to.be.revertedWith("Not agent");
101
- });
102
-
103
- it("should revert on double submission", async function () {
104
- await escrow.connect(agent).markSubmitted(TASK_ID);
105
- await expect(
106
- escrow.connect(agent).markSubmitted(TASK_ID)
107
- ).to.be.revertedWith("Wrong status");
108
- });
109
- });
110
-
111
- // ─── Release ───────────────────────────────────────────────
112
-
113
- describe("Release", function () {
114
- beforeEach(async function () {
115
- await escrow.connect(client).deposit(TASK_ID, agent.address, token, {
116
- value: ethers.parseEther("1"),
117
- });
118
- });
119
-
120
- it("should release and call buyback", async function () {
121
- await expect(escrow.connect(client).release(TASK_ID))
122
- .to.emit(escrow, "BuybackBurned")
123
- .withArgs(TASK_ID, token, ethers.parseEther("1"));
124
-
125
- expect(await mockBuyback.callCount()).to.equal(1);
126
- expect(await mockBuyback.lastToken()).to.equal(token);
127
- expect(await mockBuyback.lastValue()).to.equal(ethers.parseEther("1"));
128
-
129
- const e = await escrow.getEscrow(TASK_ID);
130
- expect(e.status).to.equal(4); // Released
131
- });
132
-
133
- it("should fallback to agent when buyback fails", async function () {
134
- await escrow.connect(admin).setBuybackHandler(await failingBuyback.getAddress());
135
-
136
- const agentBalanceBefore = await ethers.provider.getBalance(agent.address);
137
-
138
- await expect(escrow.connect(client).release(TASK_ID))
139
- .to.emit(escrow, "FallbackToAgent")
140
- .withArgs(TASK_ID, agent.address, ethers.parseEther("1"));
141
-
142
- const agentBalanceAfter = await ethers.provider.getBalance(agent.address);
143
- expect(agentBalanceAfter - agentBalanceBefore).to.equal(ethers.parseEther("1"));
144
- });
145
-
146
- it("should revert if caller is not client", async function () {
147
- await expect(
148
- escrow.connect(other).release(TASK_ID)
149
- ).to.be.revertedWith("Not client");
150
- });
151
-
152
- it("should revert on double release", async function () {
153
- await escrow.connect(client).release(TASK_ID);
154
- await expect(
155
- escrow.connect(client).release(TASK_ID)
156
- ).to.be.revertedWith("Wrong status");
157
- });
158
- });
159
-
160
- // ─── Refund ────────────────────────────────────────────────
161
-
162
- describe("Refund", function () {
163
- beforeEach(async function () {
164
- await escrow.connect(client).deposit(TASK_ID, agent.address, token, {
165
- value: ethers.parseEther("0.5"),
166
- });
167
- });
168
-
169
- it("should refund before submission", async function () {
170
- const clientBalanceBefore = await ethers.provider.getBalance(client.address);
171
-
172
- const tx = await escrow.connect(client).refund(TASK_ID);
173
- const receipt = await tx.wait();
174
- const gasUsed = receipt.gasUsed * receipt.gasPrice;
175
-
176
- await expect(tx)
177
- .to.emit(escrow, "Refunded")
178
- .withArgs(TASK_ID, client.address, ethers.parseEther("0.5"));
179
-
180
- const clientBalanceAfter = await ethers.provider.getBalance(client.address);
181
- expect(clientBalanceAfter + gasUsed - clientBalanceBefore).to.equal(
182
- ethers.parseEther("0.5")
183
- );
184
-
185
- const e = await escrow.getEscrow(TASK_ID);
186
- expect(e.status).to.equal(5); // Refunded
187
- });
188
-
189
- it("should block refund after submission", async function () {
190
- await escrow.connect(agent).markSubmitted(TASK_ID);
191
- await expect(
192
- escrow.connect(client).refund(TASK_ID)
193
- ).to.be.revertedWith("Work already submitted");
194
- });
195
-
196
- it("should revert if caller is not client", async function () {
197
- await expect(
198
- escrow.connect(other).refund(TASK_ID)
199
- ).to.be.revertedWith("Not client");
200
- });
201
- });
202
-
203
- // ─── Timeout ───────────────────────────────────────────────
204
-
205
- describe("Timeout", function () {
206
- beforeEach(async function () {
207
- await escrow.connect(client).deposit(TASK_ID, agent.address, token, {
208
- value: ethers.parseEther("1"),
209
- });
210
- });
211
-
212
- it("should release after 24h timeout", async function () {
213
- await escrow.connect(agent).markSubmitted(TASK_ID);
214
-
215
- await time.increase(TIMEOUT);
216
-
217
- expect(await escrow.isTimedOut(TASK_ID)).to.be.true;
218
-
219
- await expect(escrow.connect(other).releaseAfterTimeout(TASK_ID))
220
- .to.emit(escrow, "BuybackBurned");
221
- });
222
-
223
- it("should block release before timeout", async function () {
224
- await escrow.connect(agent).markSubmitted(TASK_ID);
225
-
226
- await expect(
227
- escrow.connect(other).releaseAfterTimeout(TASK_ID)
228
- ).to.be.revertedWith("Timeout not reached");
229
- });
230
-
231
- it("should block release if not submitted", async function () {
232
- await expect(
233
- escrow.connect(other).releaseAfterTimeout(TASK_ID)
234
- ).to.be.revertedWith("Wrong status");
235
- });
236
- });
237
-
238
- // ─── Admin ─────────────────────────────────────────────────
239
-
240
- describe("Admin", function () {
241
- it("should allow admin to set buyback handler", async function () {
242
- await escrow.connect(admin).setBuybackHandler(await failingBuyback.getAddress());
243
- expect(await escrow.buybackHandler()).to.equal(await failingBuyback.getAddress());
244
- });
245
-
246
- it("should allow admin to transfer admin", async function () {
247
- await escrow.connect(admin).transferAdmin(other.address);
248
- expect(await escrow.admin()).to.equal(other.address);
249
- });
250
-
251
- it("should revert non-admin setBuybackHandler", async function () {
252
- await expect(
253
- escrow.connect(other).setBuybackHandler(await failingBuyback.getAddress())
254
- ).to.be.revertedWith("Not admin");
255
- });
256
-
257
- it("should allow admin to setDisputeFeeBps", async function () {
258
- await escrow.connect(admin).setDisputeFeeBps(500);
259
- expect(await escrow.disputeFeeBps()).to.equal(500);
260
-
261
- await escrow.connect(admin).setDisputeFeeBps(3000);
262
- expect(await escrow.disputeFeeBps()).to.equal(3000);
263
- });
264
-
265
- it("should revert non-admin setDisputeFeeBps", async function () {
266
- await expect(
267
- escrow.connect(other).setDisputeFeeBps(500)
268
- ).to.be.revertedWith("Not admin");
269
- });
270
- });
271
-
272
- // ─── View Functions ────────────────────────────────────────
273
-
274
- describe("View Functions", function () {
275
- beforeEach(async function () {
276
- await escrow.connect(client).deposit(TASK_ID, agent.address, token, {
277
- value: ethers.parseEther("1"),
278
- });
279
- });
280
-
281
- it("isPending should return true for active escrow", async function () {
282
- expect(await escrow.isPending(TASK_ID)).to.be.true;
283
- });
284
-
285
- it("isPending should return false after release", async function () {
286
- await escrow.connect(client).release(TASK_ID);
287
- expect(await escrow.isPending(TASK_ID)).to.be.false;
288
- });
289
-
290
- it("getStatus should track status changes", async function () {
291
- expect(await escrow.getStatus(TASK_ID)).to.equal(0); // Active
292
- await escrow.connect(agent).markSubmitted(TASK_ID);
293
- expect(await escrow.getStatus(TASK_ID)).to.equal(1); // Submitted
294
- });
295
-
296
- it("isTimedOut should return false before submission", async function () {
297
- expect(await escrow.isTimedOut(TASK_ID)).to.be.false;
298
- });
299
-
300
- it("timeUntilTimeout should countdown correctly", async function () {
301
- await escrow.connect(agent).markSubmitted(TASK_ID);
302
- const remaining = await escrow.timeUntilTimeout(TASK_ID);
303
- expect(remaining).to.be.closeTo(TIMEOUT, 10);
304
- });
305
-
306
- it("getDisputeFee should return correct fee", async function () {
307
- const fee = await escrow.getDisputeFee(TASK_ID);
308
- // 10% of 1 ETH = 0.1 ETH
309
- expect(fee).to.equal(ethers.parseEther("0.1"));
310
- });
311
-
312
- it("isDisputed should return false initially", async function () {
313
- expect(await escrow.isDisputed(TASK_ID)).to.be.false;
314
- });
315
- });
316
-
317
- // ─── Dispute ───────────────────────────────────────────────
318
-
319
- describe("Dispute", function () {
320
- const escrowAmount = ethers.parseEther("1");
321
-
322
- beforeEach(async function () {
323
- await escrow.connect(client).deposit(TASK_ID, agent.address, token, {
324
- value: escrowAmount,
325
- });
326
- await escrow.connect(agent).markSubmitted(TASK_ID);
327
- });
328
-
329
- it("should allow client to dispute submitted work", async function () {
330
- const disputeFee = await escrow.getDisputeFee(TASK_ID);
331
-
332
- await expect(
333
- escrow.connect(client).dispute(TASK_ID, { value: disputeFee })
334
- )
335
- .to.emit(escrow, "Disputed")
336
- .withArgs(TASK_ID, client.address, disputeFee);
337
-
338
- expect(await escrow.getStatus(TASK_ID)).to.equal(2); // Disputed
339
- expect(await escrow.isDisputed(TASK_ID)).to.be.true;
340
- });
341
-
342
- it("should revert if not client", async function () {
343
- const disputeFee = await escrow.getDisputeFee(TASK_ID);
344
- await expect(
345
- escrow.connect(other).dispute(TASK_ID, { value: disputeFee })
346
- ).to.be.revertedWith("Not client");
347
- });
348
-
349
- it("should revert if not submitted", async function () {
350
- const taskId2 = ethers.keccak256(ethers.toUtf8Bytes("test-task-v4-2"));
351
- await escrow.connect(client).deposit(taskId2, agent.address, token, {
352
- value: escrowAmount,
353
- });
354
- // Not submitted yet
355
- const disputeFee = await escrow.getDisputeFee(taskId2);
356
- await expect(
357
- escrow.connect(client).dispute(taskId2, { value: disputeFee })
358
- ).to.be.revertedWith("Not submitted");
359
- });
360
-
361
- it("should revert if insufficient fee", async function () {
362
- const disputeFee = await escrow.getDisputeFee(TASK_ID);
363
- const insufficientFee = disputeFee - 1n;
364
- await expect(
365
- escrow.connect(client).dispute(TASK_ID, { value: insufficientFee })
366
- ).to.be.revertedWith("Insufficient dispute fee");
367
- });
368
-
369
- it("should revert if review window closed", async function () {
370
- await time.increase(TIMEOUT);
371
-
372
- const disputeFee = await escrow.getDisputeFee(TASK_ID);
373
- await expect(
374
- escrow.connect(client).dispute(TASK_ID, { value: disputeFee })
375
- ).to.be.revertedWith("Review window closed");
376
- });
377
-
378
- it("releaseAfterTimeout should revert on disputed task", async function () {
379
- const disputeFee = await escrow.getDisputeFee(TASK_ID);
380
- await escrow.connect(client).dispute(TASK_ID, { value: disputeFee });
381
-
382
- await time.increase(TIMEOUT);
383
-
384
- await expect(
385
- escrow.connect(other).releaseAfterTimeout(TASK_ID)
386
- ).to.be.revertedWith("Wrong status");
387
- });
388
- });
389
-
390
- // ─── Resolve Dispute ───────────────────────────────────────
391
-
392
- describe("Resolve Dispute", function () {
393
- const escrowAmount = ethers.parseEther("1");
394
- let disputeFee;
395
-
396
- beforeEach(async function () {
397
- await escrow.connect(client).deposit(TASK_ID, agent.address, token, {
398
- value: escrowAmount,
399
- });
400
- await escrow.connect(agent).markSubmitted(TASK_ID);
401
-
402
- disputeFee = await escrow.getDisputeFee(TASK_ID);
403
- await escrow.connect(client).dispute(TASK_ID, { value: disputeFee });
404
- });
405
-
406
- it("client wins: should refund escrow + dispute fee to client", async function () {
407
- const clientBalanceBefore = await ethers.provider.getBalance(client.address);
408
-
409
- const totalRefund = escrowAmount + disputeFee;
410
-
411
- await expect(escrow.connect(admin).resolveDispute(TASK_ID, true))
412
- .to.emit(escrow, "Refunded")
413
- .withArgs(TASK_ID, client.address, totalRefund)
414
- .and.to.emit(escrow, "DisputeResolved")
415
- .withArgs(TASK_ID, true);
416
-
417
- const clientBalanceAfter = await ethers.provider.getBalance(client.address);
418
- expect(clientBalanceAfter - clientBalanceBefore).to.equal(totalRefund);
419
-
420
- expect(await escrow.getStatus(TASK_ID)).to.equal(3); // Resolved
421
- });
422
-
423
- it("agent wins: should buyback escrow + send dispute fee to agent", async function () {
424
- const agentBalanceBefore = await ethers.provider.getBalance(agent.address);
425
-
426
- await expect(escrow.connect(admin).resolveDispute(TASK_ID, false))
427
- .to.emit(escrow, "BuybackBurned")
428
- .withArgs(TASK_ID, token, escrowAmount)
429
- .and.to.emit(escrow, "DisputeResolved")
430
- .withArgs(TASK_ID, false);
431
-
432
- // Agent receives the dispute fee
433
- const agentBalanceAfter = await ethers.provider.getBalance(agent.address);
434
- expect(agentBalanceAfter - agentBalanceBefore).to.equal(disputeFee);
435
-
436
- // Buyback was called with escrow amount
437
- expect(await mockBuyback.callCount()).to.equal(1);
438
- expect(await mockBuyback.lastToken()).to.equal(token);
439
- expect(await mockBuyback.lastValue()).to.equal(escrowAmount);
440
-
441
- expect(await escrow.getStatus(TASK_ID)).to.equal(3); // Resolved
442
- });
443
-
444
- it("should revert if not admin", async function () {
445
- await expect(
446
- escrow.connect(other).resolveDispute(TASK_ID, true)
447
- ).to.be.revertedWith("Not admin");
448
- });
449
-
450
- it("should revert if not disputed", async function () {
451
- const taskId2 = ethers.keccak256(ethers.toUtf8Bytes("test-task-v4-3"));
452
- await escrow.connect(client).deposit(taskId2, agent.address, token, {
453
- value: escrowAmount,
454
- });
455
- await escrow.connect(agent).markSubmitted(taskId2);
456
-
457
- await expect(
458
- escrow.connect(admin).resolveDispute(taskId2, true)
459
- ).to.be.revertedWith("Not disputed");
460
- });
461
- });
462
-
463
- // ─── setDisputeFeeBps ──────────────────────────────────────
464
-
465
- describe("setDisputeFeeBps", function () {
466
- it("admin can set to 500 (5%)", async function () {
467
- await escrow.connect(admin).setDisputeFeeBps(500);
468
- expect(await escrow.disputeFeeBps()).to.equal(500);
469
- });
470
-
471
- it("admin can set to 3000 (30%)", async function () {
472
- await escrow.connect(admin).setDisputeFeeBps(3000);
473
- expect(await escrow.disputeFeeBps()).to.equal(3000);
474
- });
475
-
476
- it("reverts below 500", async function () {
477
- await expect(
478
- escrow.connect(admin).setDisputeFeeBps(499)
479
- ).to.be.revertedWith("Fee 5-30%");
480
- });
481
-
482
- it("reverts above 3000", async function () {
483
- await expect(
484
- escrow.connect(admin).setDisputeFeeBps(3001)
485
- ).to.be.revertedWith("Fee 5-30%");
486
- });
487
-
488
- it("non-admin reverts", async function () {
489
- await expect(
490
- escrow.connect(other).setDisputeFeeBps(1000)
491
- ).to.be.revertedWith("Not admin");
492
- });
493
- });
494
-
495
- // ─── Integration ───────────────────────────────────────────
496
-
497
- describe("Integration", function () {
498
- const escrowAmount = ethers.parseEther("2");
499
-
500
- it("full flow: deposit -> submit -> dispute -> client wins", async function () {
501
- // Deposit
502
- await escrow.connect(client).deposit(TASK_ID, agent.address, token, {
503
- value: escrowAmount,
504
- });
505
- expect(await escrow.getStatus(TASK_ID)).to.equal(0); // Active
506
-
507
- // Submit
508
- await escrow.connect(agent).markSubmitted(TASK_ID);
509
- expect(await escrow.getStatus(TASK_ID)).to.equal(1); // Submitted
510
-
511
- // Dispute
512
- const disputeFee = await escrow.getDisputeFee(TASK_ID);
513
- await escrow.connect(client).dispute(TASK_ID, { value: disputeFee });
514
- expect(await escrow.getStatus(TASK_ID)).to.equal(2); // Disputed
515
-
516
- // Resolve (client wins)
517
- const clientBalanceBefore = await ethers.provider.getBalance(client.address);
518
- await escrow.connect(admin).resolveDispute(TASK_ID, true);
519
-
520
- const clientBalanceAfter = await ethers.provider.getBalance(client.address);
521
- const totalRefund = escrowAmount + disputeFee;
522
- expect(clientBalanceAfter - clientBalanceBefore).to.equal(totalRefund);
523
-
524
- expect(await escrow.getStatus(TASK_ID)).to.equal(3); // Resolved
525
- });
526
-
527
- it("full flow: deposit -> submit -> dispute -> agent wins", async function () {
528
- await escrow.connect(client).deposit(TASK_ID, agent.address, token, {
529
- value: escrowAmount,
530
- });
531
-
532
- await escrow.connect(agent).markSubmitted(TASK_ID);
533
-
534
- const disputeFee = await escrow.getDisputeFee(TASK_ID);
535
- await escrow.connect(client).dispute(TASK_ID, { value: disputeFee });
536
-
537
- const agentBalanceBefore = await ethers.provider.getBalance(agent.address);
538
- await escrow.connect(admin).resolveDispute(TASK_ID, false);
539
-
540
- // Agent receives dispute fee
541
- const agentBalanceAfter = await ethers.provider.getBalance(agent.address);
542
- expect(agentBalanceAfter - agentBalanceBefore).to.equal(disputeFee);
543
-
544
- // Buyback was called with escrow amount
545
- expect(await mockBuyback.callCount()).to.equal(1);
546
- expect(await mockBuyback.lastValue()).to.equal(escrowAmount);
547
-
548
- expect(await escrow.getStatus(TASK_ID)).to.equal(3); // Resolved
549
- });
550
-
551
- it("deposit -> submit -> timeout (no dispute) -> releaseAfterTimeout", async function () {
552
- await escrow.connect(client).deposit(TASK_ID, agent.address, token, {
553
- value: escrowAmount,
554
- });
555
-
556
- await escrow.connect(agent).markSubmitted(TASK_ID);
557
-
558
- // Fast forward past timeout without dispute
559
- await time.increase(TIMEOUT);
560
-
561
- await expect(escrow.connect(other).releaseAfterTimeout(TASK_ID))
562
- .to.emit(escrow, "BuybackBurned")
563
- .withArgs(TASK_ID, token, escrowAmount);
564
-
565
- expect(await escrow.getStatus(TASK_ID)).to.equal(4); // Released
566
- });
567
- });
568
- });
package/tsconfig.json DELETED
@@ -1,19 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "strict": true,
7
- "esModuleInterop": true,
8
- "skipLibCheck": true,
9
- "forceConsistentCasingInFileNames": true,
10
- "resolveJsonModule": true,
11
- "declaration": true,
12
- "declarationMap": true,
13
- "outDir": "./dist",
14
- "rootDir": "./src",
15
- "types": ["node"]
16
- },
17
- "include": ["src/**/*"],
18
- "exclude": ["node_modules", "dist"]
19
- }
package/tsup.config.ts DELETED
@@ -1,15 +0,0 @@
1
- import { defineConfig } from "tsup";
2
-
3
- export default defineConfig({
4
- entry: ["src/index.ts"],
5
- format: ["esm"],
6
- target: "node18",
7
- outDir: "dist",
8
- clean: true,
9
- splitting: false,
10
- sourcemap: true,
11
- dts: true,
12
- banner: {
13
- js: "#!/usr/bin/env node",
14
- },
15
- });