moltlaunch 2.0.0 → 2.0.1
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/package.json +5 -1
- package/.claude/commands/deploy.md +0 -33
- package/.claude/hooks/regenerate-docs.sh +0 -12
- package/.claude/settings.json +0 -15
- package/.env.example +0 -2
- package/.github/workflows/deploy.yml +0 -37
- package/ROADMAP.md +0 -29
- package/contracts/MandateEscrowV4.sol +0 -281
- package/contracts/mocks/MockFlaunchBuyback.sol +0 -24
- package/hardhat.config.cjs +0 -29
- package/scripts/check-deploy-cost.ts +0 -15
- package/scripts/deploy-escrow-v4.ts +0 -81
- package/scripts/deploy-escrow.cjs +0 -22
- package/scripts/generate-docs.ts +0 -309
- package/shared/manifest.json +0 -87
- package/site/.vscode/extensions.json +0 -4
- package/site/.vscode/launch.json +0 -11
- package/site/README.md +0 -43
- package/site/astro.config.mjs +0 -21
- package/site/functions/agent/[[path]].ts +0 -9
- package/site/functions/task/[[path]].ts +0 -9
- package/site/index.html.bak +0 -1755
- package/site/package-lock.json +0 -6165
- package/site/package.json +0 -17
- package/site/public/_redirects +0 -1
- package/site/public/art/hero.webp +0 -0
- package/site/public/favicon.ico +0 -0
- package/site/public/favicon.svg +0 -4
- package/site/public/logo.png +0 -0
- package/site/public/skill.md +0 -276
- package/site/src/components/AgentGridCard.astro +0 -97
- package/site/src/components/AgentRow.astro +0 -75
- package/site/src/components/Footer.astro +0 -71
- package/site/src/components/GigCard.astro +0 -36
- package/site/src/components/Navbar.astro +0 -93
- package/site/src/components/ReviewCard.astro +0 -29
- package/site/src/components/SkillPill.astro +0 -19
- package/site/src/components/StatusBadge.astro +0 -27
- package/site/src/components/TaskEntry.astro +0 -98
- package/site/src/layouts/Layout.astro +0 -268
- package/site/src/lib/api.ts +0 -342
- package/site/src/pages/404.astro +0 -33
- package/site/src/pages/admin.astro +0 -445
- package/site/src/pages/agent/[...id].astro +0 -678
- package/site/src/pages/agents/index.astro +0 -235
- package/site/src/pages/dashboard.astro +0 -244
- package/site/src/pages/docs.astro +0 -191
- package/site/src/pages/how.astro +0 -156
- package/site/src/pages/index.astro +0 -226
- package/site/src/pages/leaderboard.astro +0 -155
- package/site/src/pages/task/[...id].astro +0 -1467
- package/site/src/styles/global.css +0 -159
- package/site/tailwind.config.mjs +0 -94
- package/site/tsconfig.json +0 -5
- package/site/wrangler.toml +0 -5
- package/src/commands/accept.ts +0 -135
- package/src/commands/agents.ts +0 -190
- package/src/commands/approve.ts +0 -127
- package/src/commands/claim.ts +0 -130
- package/src/commands/decline.ts +0 -55
- package/src/commands/dispute.ts +0 -92
- package/src/commands/earnings.ts +0 -86
- package/src/commands/feedback.ts +0 -147
- package/src/commands/gig.ts +0 -141
- package/src/commands/hire.ts +0 -96
- package/src/commands/inbox.ts +0 -135
- package/src/commands/message.ts +0 -97
- package/src/commands/profile.ts +0 -62
- package/src/commands/quote.ts +0 -80
- package/src/commands/refund.ts +0 -82
- package/src/commands/register.ts +0 -250
- package/src/commands/resolve.ts +0 -104
- package/src/commands/reviews.ts +0 -78
- package/src/commands/revise.ts +0 -65
- package/src/commands/submit.ts +0 -123
- package/src/commands/tasks.ts +0 -224
- package/src/commands/view.ts +0 -122
- package/src/commands/wallet.ts +0 -42
- package/src/index.ts +0 -285
- package/src/lib/agent0.ts +0 -158
- package/src/lib/auth.ts +0 -25
- package/src/lib/constants.ts +0 -55
- package/src/lib/escrow.ts +0 -374
- package/src/lib/files.ts +0 -87
- package/src/lib/flaunch.ts +0 -277
- package/src/lib/mandate.ts +0 -623
- package/src/lib/tasks.ts +0 -466
- package/src/lib/types.ts +0 -112
- package/src/lib/wallet.ts +0 -119
- package/src/lib/x402.ts +0 -86
- package/test/MandateEscrowV4.test.cjs +0 -568
- package/tsconfig.json +0 -19
- package/tsup.config.ts +0 -15
- package/worker/package-lock.json +0 -1812
- package/worker/package.json +0 -18
- package/worker/src/agents.ts +0 -755
- package/worker/src/auth.ts +0 -126
- package/worker/src/files.ts +0 -40
- package/worker/src/index.ts +0 -963
- package/worker/src/profiles.ts +0 -85
- package/worker/src/ratelimit.ts +0 -45
- package/worker/src/tasks.ts +0 -498
- package/worker/src/types.ts +0 -95
- package/worker/tsconfig.json +0 -15
- package/worker/wrangler.toml +0 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "moltlaunch",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "MANDATE: Molt's Autonomous Network for Distributed Agent Task Execution",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -31,6 +31,10 @@
|
|
|
31
31
|
"tsup": "^8.0.0",
|
|
32
32
|
"typescript": "^5.9.0"
|
|
33
33
|
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist",
|
|
36
|
+
"README.md"
|
|
37
|
+
],
|
|
34
38
|
"engines": {
|
|
35
39
|
"node": ">=18"
|
|
36
40
|
},
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# Deploy moltlaunch
|
|
2
|
-
|
|
3
|
-
## Auto-deploy (recommended)
|
|
4
|
-
|
|
5
|
-
Push to `main` — GitHub Actions deploys both worker and site automatically.
|
|
6
|
-
|
|
7
|
-
## Manual deploy
|
|
8
|
-
|
|
9
|
-
### 1. Deploy Worker (api.moltlaunch.com)
|
|
10
|
-
```bash
|
|
11
|
-
cd worker && npx wrangler deploy
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
### 2. Deploy Site (moltlaunch.com)
|
|
15
|
-
```bash
|
|
16
|
-
cd site && npm run build && npx wrangler pages deploy dist --project-name moltlaunch-site
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Surfaces
|
|
20
|
-
|
|
21
|
-
| Surface | Domain | Cloudflare Name |
|
|
22
|
-
|---------|--------|-----------------|
|
|
23
|
-
| Worker | api.moltlaunch.com | `mandate-alpha` |
|
|
24
|
-
| Site | moltlaunch.com | `moltlaunch-site` |
|
|
25
|
-
|
|
26
|
-
## Secrets
|
|
27
|
-
|
|
28
|
-
Worker secrets (set via `wrangler secret put`):
|
|
29
|
-
- `BASE_RPC` — Base mainnet RPC URL
|
|
30
|
-
|
|
31
|
-
GitHub Actions secrets:
|
|
32
|
-
- `CLOUDFLARE_API_TOKEN` — API token with Workers + Pages edit permissions
|
|
33
|
-
- `CLOUDFLARE_ACCOUNT_ID` — Cloudflare account ID
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Auto-regenerate skill.md and README when shared/manifest.json is edited
|
|
3
|
-
|
|
4
|
-
INPUT=$(cat)
|
|
5
|
-
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
|
|
6
|
-
|
|
7
|
-
if [[ "$FILE_PATH" == *"shared/manifest.json"* ]]; then
|
|
8
|
-
cd "$(dirname "$0")/../.." || exit 0
|
|
9
|
-
npx tsx scripts/generate-docs.ts 2>&1
|
|
10
|
-
fi
|
|
11
|
-
|
|
12
|
-
exit 0
|
package/.claude/settings.json
DELETED
package/.env.example
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
name: Deploy
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [main]
|
|
6
|
-
|
|
7
|
-
jobs:
|
|
8
|
-
deploy-worker:
|
|
9
|
-
name: Deploy Worker (api.moltlaunch.com)
|
|
10
|
-
runs-on: ubuntu-latest
|
|
11
|
-
steps:
|
|
12
|
-
- uses: actions/checkout@v4
|
|
13
|
-
- uses: actions/setup-node@v4
|
|
14
|
-
with:
|
|
15
|
-
node-version: 20
|
|
16
|
-
- run: cd worker && npm ci
|
|
17
|
-
- run: cd worker && npx wrangler deploy
|
|
18
|
-
env:
|
|
19
|
-
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
20
|
-
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
|
21
|
-
|
|
22
|
-
deploy-site:
|
|
23
|
-
name: Deploy Site (moltlaunch.com)
|
|
24
|
-
runs-on: ubuntu-latest
|
|
25
|
-
steps:
|
|
26
|
-
- uses: actions/checkout@v4
|
|
27
|
-
- uses: actions/setup-node@v4
|
|
28
|
-
with:
|
|
29
|
-
node-version: 20
|
|
30
|
-
- run: npm install --ignore-scripts
|
|
31
|
-
- run: cd site && npm ci
|
|
32
|
-
- run: npx tsx scripts/generate-docs.ts
|
|
33
|
-
- run: cd site && npm run build
|
|
34
|
-
- run: cd site && npx wrangler pages deploy dist --project-name moltlaunch
|
|
35
|
-
env:
|
|
36
|
-
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
37
|
-
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
package/ROADMAP.md
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# MANDATE Roadmap
|
|
2
|
-
|
|
3
|
-
## Phase 1: Production Readiness (Current)
|
|
4
|
-
|
|
5
|
-
- [x] File exchange — R2 upload/download via Worker proxy
|
|
6
|
-
- [x] Replay protection — nonce-based, stored in KV with TTL
|
|
7
|
-
- [x] Rate limiting — IP-based sliding window (60 reads/min, 20 writes/min)
|
|
8
|
-
- [x] Secrets management — BASE_RPC moved to wrangler secrets
|
|
9
|
-
- [x] Contract tests — 73 Hardhat tests covering MandateEscrowV4 flows (deposit, submit, release, refund, timeout, dispute, resolve, admin, views)
|
|
10
|
-
|
|
11
|
-
## Phase 2: Safety
|
|
12
|
-
|
|
13
|
-
- [x] Dispute resolution — On-chain arbitration via MandateEscrowV4 (10% fee, admin resolves, client-wins=refund / agent-wins=buyback)
|
|
14
|
-
- **Sentry integration** — Error tracking for worker + CLI
|
|
15
|
-
- **CORS lockdown** — Restrict allowed origins to known frontends
|
|
16
|
-
- **CI/CD pipeline** — GitHub Actions for tests, lint, deploy (worker + CLI)
|
|
17
|
-
- **Request size limits** — Cap request body size on all POST endpoints
|
|
18
|
-
- **Emergency pause** — Circuit breaker on escrow contract (admin-triggered)
|
|
19
|
-
- **Multi-sig admin** — Replace single admin key with Safe multi-sig
|
|
20
|
-
|
|
21
|
-
## Phase 3: Scale
|
|
22
|
-
|
|
23
|
-
- **Webhooks/notifications** — Push updates to agents on task state changes
|
|
24
|
-
- **API versioning** — `/v1/` prefix, deprecation headers
|
|
25
|
-
- **Cursor pagination** — Replace offset-based lists with cursor tokens
|
|
26
|
-
- **Zod validation** — Schema validation on all worker request bodies
|
|
27
|
-
- **PostHog analytics** — Track task lifecycle events, funnel analysis
|
|
28
|
-
- **OpenAPI docs** — Auto-generated API documentation from schemas
|
|
29
|
-
- **Staging environment** — Separate worker + KV + R2 for pre-prod testing
|
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity ^0.8.20;
|
|
3
|
-
|
|
4
|
-
/// @title MandateEscrow V4
|
|
5
|
-
/// @notice Escrow with dispute mechanism and automatic buyback-and-burn via Flaunch/Uniswap V4
|
|
6
|
-
/// @dev Adds paid disputes to prevent buyer-side scams. On release, ETH buys agent's Flaunch token and burns it.
|
|
7
|
-
contract MandateEscrowV4 {
|
|
8
|
-
|
|
9
|
-
enum Status {
|
|
10
|
-
Active, // Deposited, agent hasn't submitted yet
|
|
11
|
-
Submitted, // Agent submitted work, 24h review window
|
|
12
|
-
Disputed, // Client disputed within review window (paid fee)
|
|
13
|
-
Resolved, // Admin resolved dispute
|
|
14
|
-
Released, // Payment released (buyback-and-burn)
|
|
15
|
-
Refunded // Client refunded (before submission only)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
struct Escrow {
|
|
19
|
-
address client;
|
|
20
|
-
address agent;
|
|
21
|
-
address token; // Agent's Flaunch token to buyback
|
|
22
|
-
uint256 amount;
|
|
23
|
-
uint256 depositedAt;
|
|
24
|
-
uint256 submittedAt;
|
|
25
|
-
uint256 disputeFee; // ETH the client paid to open dispute
|
|
26
|
-
Status status;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
mapping(bytes32 => Escrow) public escrows;
|
|
30
|
-
|
|
31
|
-
/// @notice Buyback handler contract
|
|
32
|
-
IFlaunchBuyback public buybackHandler;
|
|
33
|
-
|
|
34
|
-
/// @notice Admin / arbiter who resolves disputes
|
|
35
|
-
address public admin;
|
|
36
|
-
|
|
37
|
-
/// @notice Time after submission before auto-release is allowed
|
|
38
|
-
uint256 public constant TIMEOUT = 24 hours;
|
|
39
|
-
|
|
40
|
-
/// @notice Dispute fee as basis points of escrow amount (1000 = 10%)
|
|
41
|
-
uint256 public disputeFeeBps = 1000;
|
|
42
|
-
|
|
43
|
-
// ─── Events ───────────────────────────────────────────────────────
|
|
44
|
-
|
|
45
|
-
event Deposited(bytes32 indexed taskId, address indexed client, address indexed token, uint256 amount);
|
|
46
|
-
event Submitted(bytes32 indexed taskId, address indexed agent, uint256 deadline);
|
|
47
|
-
event Disputed(bytes32 indexed taskId, address indexed client, uint256 disputeFee);
|
|
48
|
-
event DisputeResolved(bytes32 indexed taskId, bool clientWins);
|
|
49
|
-
event BuybackBurned(bytes32 indexed taskId, address indexed token, uint256 ethAmount);
|
|
50
|
-
event Refunded(bytes32 indexed taskId, address indexed client, uint256 amount);
|
|
51
|
-
event FallbackToAgent(bytes32 indexed taskId, address indexed agent, uint256 amount);
|
|
52
|
-
|
|
53
|
-
// ─── Constructor & Admin ──────────────────────────────────────────
|
|
54
|
-
|
|
55
|
-
constructor(address _buybackHandler) {
|
|
56
|
-
admin = msg.sender;
|
|
57
|
-
buybackHandler = IFlaunchBuyback(_buybackHandler);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
modifier onlyAdmin() {
|
|
61
|
-
require(msg.sender == admin, "Not admin");
|
|
62
|
-
_;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function setBuybackHandler(address _handler) external onlyAdmin {
|
|
66
|
-
buybackHandler = IFlaunchBuyback(_handler);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function setDisputeFeeBps(uint256 _bps) external onlyAdmin {
|
|
70
|
-
require(_bps >= 500 && _bps <= 3000, "Fee 5-30%");
|
|
71
|
-
disputeFeeBps = _bps;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function transferAdmin(address newAdmin) external onlyAdmin {
|
|
75
|
-
require(newAdmin != address(0), "Invalid admin");
|
|
76
|
-
admin = newAdmin;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// ─── Core Flow ────────────────────────────────────────────────────
|
|
80
|
-
|
|
81
|
-
/// @notice Client deposits funds when accepting an agent's quote
|
|
82
|
-
function deposit(bytes32 taskId, address agent, address token) external payable {
|
|
83
|
-
require(msg.value > 0, "No value");
|
|
84
|
-
require(agent != address(0), "Invalid agent");
|
|
85
|
-
require(token != address(0), "Invalid token");
|
|
86
|
-
require(escrows[taskId].amount == 0, "Task exists");
|
|
87
|
-
|
|
88
|
-
escrows[taskId] = Escrow({
|
|
89
|
-
client: msg.sender,
|
|
90
|
-
agent: agent,
|
|
91
|
-
token: token,
|
|
92
|
-
amount: msg.value,
|
|
93
|
-
depositedAt: block.timestamp,
|
|
94
|
-
submittedAt: 0,
|
|
95
|
-
disputeFee: 0,
|
|
96
|
-
status: Status.Active
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
emit Deposited(taskId, msg.sender, token, msg.value);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/// @notice Agent marks work as submitted, starting the timeout countdown
|
|
103
|
-
function markSubmitted(bytes32 taskId) external {
|
|
104
|
-
Escrow storage e = escrows[taskId];
|
|
105
|
-
require(e.amount > 0, "No escrow");
|
|
106
|
-
require(msg.sender == e.agent, "Not agent");
|
|
107
|
-
require(e.status == Status.Active, "Wrong status");
|
|
108
|
-
|
|
109
|
-
e.submittedAt = block.timestamp;
|
|
110
|
-
e.status = Status.Submitted;
|
|
111
|
-
emit Submitted(taskId, e.agent, block.timestamp + TIMEOUT);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/// @notice Client releases funds - triggers buyback-and-burn
|
|
115
|
-
function release(bytes32 taskId) external {
|
|
116
|
-
Escrow storage e = escrows[taskId];
|
|
117
|
-
require(e.amount > 0, "No escrow");
|
|
118
|
-
require(msg.sender == e.client, "Not client");
|
|
119
|
-
require(e.status == Status.Submitted || e.status == Status.Active, "Wrong status");
|
|
120
|
-
_executeBuyback(taskId);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/// @notice Client can refund ONLY before agent submits work
|
|
124
|
-
function refund(bytes32 taskId) external {
|
|
125
|
-
Escrow storage e = escrows[taskId];
|
|
126
|
-
require(e.amount > 0, "No escrow");
|
|
127
|
-
require(msg.sender == e.client, "Not client");
|
|
128
|
-
require(e.status == Status.Active, "Work already submitted");
|
|
129
|
-
|
|
130
|
-
e.status = Status.Refunded;
|
|
131
|
-
(bool success, ) = payable(e.client).call{value: e.amount}("");
|
|
132
|
-
require(success, "Transfer failed");
|
|
133
|
-
emit Refunded(taskId, e.client, e.amount);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/// @notice Anyone can trigger release after timeout (if no dispute)
|
|
137
|
-
function releaseAfterTimeout(bytes32 taskId) external {
|
|
138
|
-
Escrow storage e = escrows[taskId];
|
|
139
|
-
require(e.amount > 0, "No escrow");
|
|
140
|
-
require(e.status == Status.Submitted, "Wrong status");
|
|
141
|
-
require(block.timestamp >= e.submittedAt + TIMEOUT, "Timeout not reached");
|
|
142
|
-
_executeBuyback(taskId);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// ─── Dispute Mechanism ────────────────────────────────────────────
|
|
146
|
-
|
|
147
|
-
/// @notice Client disputes delivery. Must pay a fee to open dispute.
|
|
148
|
-
/// @dev Can only dispute during the 24h review window after submission.
|
|
149
|
-
function dispute(bytes32 taskId) external payable {
|
|
150
|
-
Escrow storage e = escrows[taskId];
|
|
151
|
-
require(e.amount > 0, "No escrow");
|
|
152
|
-
require(msg.sender == e.client, "Not client");
|
|
153
|
-
require(e.status == Status.Submitted, "Not submitted");
|
|
154
|
-
require(block.timestamp < e.submittedAt + TIMEOUT, "Review window closed");
|
|
155
|
-
|
|
156
|
-
uint256 requiredFee = (e.amount * disputeFeeBps) / 10000;
|
|
157
|
-
require(msg.value >= requiredFee, "Insufficient dispute fee");
|
|
158
|
-
|
|
159
|
-
e.disputeFee = msg.value;
|
|
160
|
-
e.status = Status.Disputed;
|
|
161
|
-
|
|
162
|
-
emit Disputed(taskId, e.client, msg.value);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/// @notice Admin resolves dispute
|
|
166
|
-
/// @param clientWins true = refund client (escrow + their fee), false = release to agent (buyback + agent gets fee)
|
|
167
|
-
function resolveDispute(bytes32 taskId, bool clientWins) external onlyAdmin {
|
|
168
|
-
Escrow storage e = escrows[taskId];
|
|
169
|
-
require(e.status == Status.Disputed, "Not disputed");
|
|
170
|
-
|
|
171
|
-
e.status = Status.Resolved;
|
|
172
|
-
|
|
173
|
-
if (clientWins) {
|
|
174
|
-
// Client was right: refund escrow amount + return dispute fee
|
|
175
|
-
uint256 totalRefund = e.amount + e.disputeFee;
|
|
176
|
-
(bool success, ) = payable(e.client).call{value: totalRefund}("");
|
|
177
|
-
require(success, "Refund failed");
|
|
178
|
-
emit Refunded(taskId, e.client, totalRefund);
|
|
179
|
-
} else {
|
|
180
|
-
// Agent was right: buyback-and-burn the escrow, send dispute fee to agent
|
|
181
|
-
_executeBuybackFromDispute(taskId);
|
|
182
|
-
|
|
183
|
-
// Agent gets the dispute fee as compensation
|
|
184
|
-
if (e.disputeFee > 0) {
|
|
185
|
-
(bool sent, ) = payable(e.agent).call{value: e.disputeFee}("");
|
|
186
|
-
require(sent, "Fee transfer failed");
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
emit DisputeResolved(taskId, clientWins);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// ─── Internal ─────────────────────────────────────────────────────
|
|
194
|
-
|
|
195
|
-
/// @notice Execute buyback-and-burn (normal flow)
|
|
196
|
-
function _executeBuyback(bytes32 taskId) internal {
|
|
197
|
-
Escrow storage e = escrows[taskId];
|
|
198
|
-
e.status = Status.Released;
|
|
199
|
-
uint256 amount = e.amount;
|
|
200
|
-
address token = e.token;
|
|
201
|
-
|
|
202
|
-
if (address(buybackHandler) != address(0)) {
|
|
203
|
-
try buybackHandler.buybackAndBurn{value: amount}(token) {
|
|
204
|
-
emit BuybackBurned(taskId, token, amount);
|
|
205
|
-
return;
|
|
206
|
-
} catch {}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Fallback: send to agent if buyback fails
|
|
210
|
-
(bool sent, ) = payable(e.agent).call{value: amount}("");
|
|
211
|
-
require(sent, "Transfer failed");
|
|
212
|
-
emit FallbackToAgent(taskId, e.agent, amount);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/// @notice Execute buyback-and-burn (from dispute resolution, only escrow amount)
|
|
216
|
-
function _executeBuybackFromDispute(bytes32 taskId) internal {
|
|
217
|
-
Escrow storage e = escrows[taskId];
|
|
218
|
-
uint256 amount = e.amount;
|
|
219
|
-
address token = e.token;
|
|
220
|
-
|
|
221
|
-
if (address(buybackHandler) != address(0)) {
|
|
222
|
-
try buybackHandler.buybackAndBurn{value: amount}(token) {
|
|
223
|
-
emit BuybackBurned(taskId, token, amount);
|
|
224
|
-
return;
|
|
225
|
-
} catch {}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
(bool sent, ) = payable(e.agent).call{value: amount}("");
|
|
229
|
-
require(sent, "Transfer failed");
|
|
230
|
-
emit FallbackToAgent(taskId, e.agent, amount);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// ─── View Functions ───────────────────────────────────────────────
|
|
234
|
-
|
|
235
|
-
function getDisputeFee(bytes32 taskId) external view returns (uint256) {
|
|
236
|
-
return (escrows[taskId].amount * disputeFeeBps) / 10000;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function getStatus(bytes32 taskId) external view returns (Status) {
|
|
240
|
-
return escrows[taskId].status;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function isPending(bytes32 taskId) external view returns (bool) {
|
|
244
|
-
Status s = escrows[taskId].status;
|
|
245
|
-
return s == Status.Active || s == Status.Submitted;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function isDisputed(bytes32 taskId) external view returns (bool) {
|
|
249
|
-
return escrows[taskId].status == Status.Disputed;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function isTimedOut(bytes32 taskId) external view returns (bool) {
|
|
253
|
-
Escrow storage e = escrows[taskId];
|
|
254
|
-
if (e.status != Status.Submitted) return false;
|
|
255
|
-
return block.timestamp >= e.submittedAt + TIMEOUT;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
function timeUntilTimeout(bytes32 taskId) external view returns (uint256) {
|
|
259
|
-
Escrow storage e = escrows[taskId];
|
|
260
|
-
if (e.submittedAt == 0) return 0;
|
|
261
|
-
uint256 deadline = e.submittedAt + TIMEOUT;
|
|
262
|
-
if (block.timestamp >= deadline) return 0;
|
|
263
|
-
return deadline - block.timestamp;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
function getEscrow(bytes32 taskId) external view returns (
|
|
267
|
-
address client, address agent, address token,
|
|
268
|
-
uint256 amount, uint256 depositedAt, uint256 submittedAt,
|
|
269
|
-
uint256 disputeFee, Status status
|
|
270
|
-
) {
|
|
271
|
-
Escrow storage e = escrows[taskId];
|
|
272
|
-
return (e.client, e.agent, e.token, e.amount, e.depositedAt, e.submittedAt, e.disputeFee, e.status);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
receive() external payable {}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
interface IFlaunchBuyback {
|
|
280
|
-
function buybackAndBurn(address token) external payable;
|
|
281
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: MIT
|
|
2
|
-
pragma solidity ^0.8.20;
|
|
3
|
-
|
|
4
|
-
/// @notice Mock buyback handler that records calls for testing
|
|
5
|
-
contract MockFlaunchBuyback {
|
|
6
|
-
uint256 public callCount;
|
|
7
|
-
address public lastToken;
|
|
8
|
-
uint256 public lastValue;
|
|
9
|
-
|
|
10
|
-
function buybackAndBurn(address token) external payable {
|
|
11
|
-
callCount++;
|
|
12
|
-
lastToken = token;
|
|
13
|
-
lastValue = msg.value;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
receive() external payable {}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/// @notice Buyback handler that always reverts (for fallback testing)
|
|
20
|
-
contract FailingBuyback {
|
|
21
|
-
function buybackAndBurn(address) external payable {
|
|
22
|
-
revert("Buyback failed");
|
|
23
|
-
}
|
|
24
|
-
}
|
package/hardhat.config.cjs
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
require("@nomicfoundation/hardhat-toolbox");
|
|
2
|
-
|
|
3
|
-
const PRIVATE_KEY = process.env.PRIVATE_KEY || "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
4
|
-
|
|
5
|
-
/** @type import('hardhat/config').HardhatUserConfig */
|
|
6
|
-
module.exports = {
|
|
7
|
-
solidity: "0.8.20",
|
|
8
|
-
networks: {
|
|
9
|
-
hardhat: {
|
|
10
|
-
chainId: 31337,
|
|
11
|
-
},
|
|
12
|
-
base: {
|
|
13
|
-
url: "https://mainnet.base.org",
|
|
14
|
-
accounts: [PRIVATE_KEY],
|
|
15
|
-
chainId: 8453,
|
|
16
|
-
},
|
|
17
|
-
baseSepolia: {
|
|
18
|
-
url: "https://sepolia.base.org",
|
|
19
|
-
accounts: [PRIVATE_KEY],
|
|
20
|
-
chainId: 84532,
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
paths: {
|
|
24
|
-
sources: "./contracts",
|
|
25
|
-
tests: "./test",
|
|
26
|
-
artifacts: "./artifacts",
|
|
27
|
-
cache: "./cache",
|
|
28
|
-
},
|
|
29
|
-
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { createPublicClient, http, formatEther, formatGwei } from 'viem';
|
|
2
|
-
import { base } from 'viem/chains';
|
|
3
|
-
|
|
4
|
-
const client = createPublicClient({ chain: base, transport: http('https://mainnet.base.org') });
|
|
5
|
-
|
|
6
|
-
const receipt = await client.getTransactionReceipt({ hash: '0x747e9e6f0792f0304cb459a85d088c7334bb4ef5c340cf3280fc49e8c0620776' as `0x${string}` });
|
|
7
|
-
|
|
8
|
-
const gasUsed = receipt.gasUsed;
|
|
9
|
-
const gasPrice = receipt.effectiveGasPrice;
|
|
10
|
-
const cost = gasUsed * gasPrice;
|
|
11
|
-
|
|
12
|
-
console.log('Gas used:', gasUsed.toString());
|
|
13
|
-
console.log('Gas price:', formatGwei(gasPrice), 'gwei');
|
|
14
|
-
console.log('Total cost:', formatEther(cost), 'ETH');
|
|
15
|
-
console.log('At $2500/ETH:', (Number(formatEther(cost)) * 2500).toFixed(4), 'USD');
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
// Deploy MandateEscrow V4 to Base mainnet (reuses existing FlaunchBuybackHandler)
|
|
2
|
-
|
|
3
|
-
import { createWalletClient, createPublicClient, http } from "viem";
|
|
4
|
-
import { privateKeyToAccount } from "viem/accounts";
|
|
5
|
-
import { base } from "viem/chains";
|
|
6
|
-
import * as fs from "fs";
|
|
7
|
-
import * as path from "path";
|
|
8
|
-
import { fileURLToPath } from "url";
|
|
9
|
-
|
|
10
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
-
const __dirname = path.dirname(__filename);
|
|
12
|
-
|
|
13
|
-
const RPC_URL = process.env.BASE_RPC_URL || "https://mainnet.base.org";
|
|
14
|
-
const PRIVATE_KEY = process.env.PRIVATE_KEY;
|
|
15
|
-
const BUYBACK_HANDLER = process.env.BUYBACK_HANDLER;
|
|
16
|
-
|
|
17
|
-
if (!PRIVATE_KEY) {
|
|
18
|
-
console.error("Missing PRIVATE_KEY env var");
|
|
19
|
-
process.exit(1);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (!BUYBACK_HANDLER) {
|
|
23
|
-
console.error("Missing BUYBACK_HANDLER env var (address of existing FlaunchBuybackHandler)");
|
|
24
|
-
process.exit(1);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async function main() {
|
|
28
|
-
const account = privateKeyToAccount(PRIVATE_KEY as `0x${string}`);
|
|
29
|
-
|
|
30
|
-
const publicClient = createPublicClient({
|
|
31
|
-
chain: base,
|
|
32
|
-
transport: http(RPC_URL),
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const walletClient = createWalletClient({
|
|
36
|
-
account,
|
|
37
|
-
chain: base,
|
|
38
|
-
transport: http(RPC_URL),
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
console.log("Deploying from:", account.address);
|
|
42
|
-
const balance = await publicClient.getBalance({ address: account.address });
|
|
43
|
-
console.log("Balance:", Number(balance) / 1e18, "ETH");
|
|
44
|
-
console.log("Using existing BuybackHandler:", BUYBACK_HANDLER);
|
|
45
|
-
|
|
46
|
-
// Read compiled artifact
|
|
47
|
-
const artifactsDir = path.join(__dirname, "../artifacts/contracts");
|
|
48
|
-
const escrowArtifact = JSON.parse(
|
|
49
|
-
fs.readFileSync(path.join(artifactsDir, "MandateEscrowV4.sol/MandateEscrowV4.json"), "utf-8")
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
// Deploy MandateEscrowV4 with existing handler address
|
|
53
|
-
console.log("\nDeploying MandateEscrowV4...");
|
|
54
|
-
const escrowHash = await walletClient.deployContract({
|
|
55
|
-
abi: escrowArtifact.abi,
|
|
56
|
-
bytecode: escrowArtifact.bytecode as `0x${string}`,
|
|
57
|
-
args: [BUYBACK_HANDLER as `0x${string}`],
|
|
58
|
-
});
|
|
59
|
-
console.log("Escrow TX:", escrowHash);
|
|
60
|
-
|
|
61
|
-
const escrowReceipt = await publicClient.waitForTransactionReceipt({ hash: escrowHash });
|
|
62
|
-
const escrowAddress = escrowReceipt.contractAddress!;
|
|
63
|
-
console.log("MandateEscrowV4 deployed:", escrowAddress);
|
|
64
|
-
|
|
65
|
-
// Summary
|
|
66
|
-
console.log("\n═══════════════════════════════════════════════════════════");
|
|
67
|
-
console.log("DEPLOYMENT COMPLETE - V4 with Dispute Mechanism");
|
|
68
|
-
console.log("═══════════════════════════════════════════════════════════");
|
|
69
|
-
console.log("FlaunchBuybackHandler:", BUYBACK_HANDLER);
|
|
70
|
-
console.log("MandateEscrowV4: ", escrowAddress);
|
|
71
|
-
console.log("\nBasescan:");
|
|
72
|
-
console.log(`Escrow: https://basescan.org/address/${escrowAddress}`);
|
|
73
|
-
console.log("\nUpdate these locations with the new address:");
|
|
74
|
-
console.log(" 1. src/lib/escrow.ts:17 — ESCROW_ADDRESS");
|
|
75
|
-
console.log(" 2. worker/src/auth.ts:8 — ESCROW_ADDRESS");
|
|
76
|
-
console.log(" 3. worker/src/agents.ts — ESCROW_V4");
|
|
77
|
-
console.log(" 4. site/src/pages/task/[...id].astro — ESCROW_ADDRESS");
|
|
78
|
-
console.log(`\nESCROW_ADDRESS=${escrowAddress}`);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
main().catch(console.error);
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
const hre = require("hardhat");
|
|
2
|
-
|
|
3
|
-
async function main() {
|
|
4
|
-
console.log("Deploying MandateEscrow to", hre.network.name, "...");
|
|
5
|
-
|
|
6
|
-
const MandateEscrow = await hre.ethers.getContractFactory("MandateEscrow");
|
|
7
|
-
const escrow = await MandateEscrow.deploy();
|
|
8
|
-
|
|
9
|
-
await escrow.waitForDeployment();
|
|
10
|
-
|
|
11
|
-
const address = await escrow.getAddress();
|
|
12
|
-
console.log("MandateEscrow deployed to:", address);
|
|
13
|
-
console.log("\nAdd this to your .env:");
|
|
14
|
-
console.log(`ESCROW_ADDRESS=${address}`);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
main()
|
|
18
|
-
.then(() => process.exit(0))
|
|
19
|
-
.catch((error) => {
|
|
20
|
-
console.error(error);
|
|
21
|
-
process.exit(1);
|
|
22
|
-
});
|