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.
- package/README.md +2 -2
- package/dist/index.js +18 -18
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
- 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/src/lib/agent0.ts
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
// Agent0 SDK wrapper for MANDATE
|
|
2
|
-
// Uses official ERC-8004 SDK: https://github.com/agent0lab/agent0-ts
|
|
3
|
-
|
|
4
|
-
import { SDK } from "agent0-sdk";
|
|
5
|
-
import { BASE_RPC_URL } from "./constants.js";
|
|
6
|
-
import type { Wallet } from "./types.js";
|
|
7
|
-
|
|
8
|
-
const BASE_CHAIN_ID = 8453;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Get read-only SDK instance (no private key needed)
|
|
12
|
-
*/
|
|
13
|
-
export function getReadOnlySDK() {
|
|
14
|
-
return new SDK({
|
|
15
|
-
chainId: BASE_CHAIN_ID,
|
|
16
|
-
rpcUrl: BASE_RPC_URL,
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Get SDK instance with signing capability
|
|
22
|
-
*/
|
|
23
|
-
export function getSDK(wallet: Wallet) {
|
|
24
|
-
return new SDK({
|
|
25
|
-
chainId: BASE_CHAIN_ID,
|
|
26
|
-
rpcUrl: BASE_RPC_URL,
|
|
27
|
-
privateKey: wallet.privateKey,
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Search for agents on Base
|
|
33
|
-
*/
|
|
34
|
-
export async function searchAgents(options?: {
|
|
35
|
-
name?: string;
|
|
36
|
-
active?: boolean;
|
|
37
|
-
x402support?: boolean;
|
|
38
|
-
limit?: number;
|
|
39
|
-
}) {
|
|
40
|
-
const sdk = getReadOnlySDK();
|
|
41
|
-
|
|
42
|
-
const results = await sdk.searchAgents({
|
|
43
|
-
name: options?.name,
|
|
44
|
-
active: options?.active,
|
|
45
|
-
x402support: options?.x402support,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
// Limit results
|
|
49
|
-
const limit = options?.limit || 20;
|
|
50
|
-
return results.slice(0, limit);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Get a single agent by ID
|
|
55
|
-
*/
|
|
56
|
-
export async function getAgentById(agentId: string) {
|
|
57
|
-
const sdk = getReadOnlySDK();
|
|
58
|
-
|
|
59
|
-
// Format: chainId:agentId or just agentId (defaults to current chain)
|
|
60
|
-
const fullId = agentId.includes(":") ? agentId : `${BASE_CHAIN_ID}:${agentId}`;
|
|
61
|
-
|
|
62
|
-
return sdk.getAgent(fullId);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Register a new agent
|
|
67
|
-
*/
|
|
68
|
-
export async function registerAgent(
|
|
69
|
-
wallet: Wallet,
|
|
70
|
-
config: {
|
|
71
|
-
name: string;
|
|
72
|
-
description: string;
|
|
73
|
-
image?: string;
|
|
74
|
-
endpoint?: string; // x402 endpoint
|
|
75
|
-
skills?: string[];
|
|
76
|
-
active?: boolean;
|
|
77
|
-
metadata?: Record<string, string>; // Custom metadata (flaunchToken, priceWei, etc.)
|
|
78
|
-
}
|
|
79
|
-
) {
|
|
80
|
-
const sdk = getSDK(wallet);
|
|
81
|
-
|
|
82
|
-
// Create agent
|
|
83
|
-
const agent = sdk.createAgent(
|
|
84
|
-
config.name,
|
|
85
|
-
config.description,
|
|
86
|
-
config.image || ""
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
// Configure x402 support if endpoint provided
|
|
90
|
-
if (config.endpoint) {
|
|
91
|
-
// Set as A2A endpoint (x402 compatible)
|
|
92
|
-
await agent.setA2A(config.endpoint);
|
|
93
|
-
agent.setTrust(true, false, false); // reputation-based trust
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Build metadata object
|
|
97
|
-
const metadata: Record<string, string> = {};
|
|
98
|
-
|
|
99
|
-
// Add skills to metadata
|
|
100
|
-
if (config.skills && config.skills.length > 0) {
|
|
101
|
-
metadata.skills = config.skills.join(",");
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Add custom metadata (flaunchToken, priceWei, symbol, etc.)
|
|
105
|
-
if (config.metadata) {
|
|
106
|
-
Object.assign(metadata, config.metadata);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Set all metadata at once
|
|
110
|
-
if (Object.keys(metadata).length > 0) {
|
|
111
|
-
agent.setMetadata(metadata);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
agent.setActive(config.active ?? true);
|
|
115
|
-
|
|
116
|
-
// Register using IPFS (Agent0 SDK handles IPFS upload)
|
|
117
|
-
// Note: This uses the default IPFS node, configure with pinata for production
|
|
118
|
-
const txHandle = await agent.registerIPFS();
|
|
119
|
-
|
|
120
|
-
// Wait for transaction to be mined
|
|
121
|
-
const { receipt } = await txHandle.waitMined({ confirmations: 1 });
|
|
122
|
-
|
|
123
|
-
// After registration, agent has ID and URI
|
|
124
|
-
const agentId = agent.agentId;
|
|
125
|
-
const agentURI = agent.agentURI;
|
|
126
|
-
|
|
127
|
-
if (!agentId) {
|
|
128
|
-
throw new Error("Registration failed: no agent ID returned");
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return {
|
|
132
|
-
agentId,
|
|
133
|
-
agentURI: agentURI || "",
|
|
134
|
-
txHash: txHandle.hash,
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Give feedback to an agent
|
|
140
|
-
*/
|
|
141
|
-
export async function giveFeedback(
|
|
142
|
-
wallet: Wallet,
|
|
143
|
-
agentId: string,
|
|
144
|
-
score: number,
|
|
145
|
-
tag?: string
|
|
146
|
-
): Promise<string> {
|
|
147
|
-
const sdk = getSDK(wallet);
|
|
148
|
-
|
|
149
|
-
const fullId = agentId.includes(":") ? agentId : `${BASE_CHAIN_ID}:${agentId}`;
|
|
150
|
-
|
|
151
|
-
// Give feedback (score 0-100)
|
|
152
|
-
const txHandle = await sdk.giveFeedback(fullId, score, tag || "mandate");
|
|
153
|
-
|
|
154
|
-
// Wait for transaction to be mined
|
|
155
|
-
await txHandle.waitMined({ confirmations: 1 });
|
|
156
|
-
|
|
157
|
-
return txHandle.hash;
|
|
158
|
-
}
|
package/src/lib/auth.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
// CLI-side message signing for authenticated API calls
|
|
2
|
-
// Signs EIP-191 personal messages: moltlaunch:<action>:<taskId>:<timestamp>:<nonce>
|
|
3
|
-
|
|
4
|
-
import { randomUUID } from "crypto";
|
|
5
|
-
import { privateKeyToAccount } from "viem/accounts";
|
|
6
|
-
import type { Wallet } from "./types.js";
|
|
7
|
-
|
|
8
|
-
/** Build the message that must be signed (must match worker/src/auth.ts) */
|
|
9
|
-
export function buildAuthMessage(action: string, taskId: string, timestamp: number, nonce: string): string {
|
|
10
|
-
return `moltlaunch:${action}:${taskId}:${timestamp}:${nonce}`;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/** Sign an action with the wallet's private key. Includes a random nonce for replay protection. */
|
|
14
|
-
export async function signAction(
|
|
15
|
-
wallet: Wallet,
|
|
16
|
-
action: string,
|
|
17
|
-
taskId: string,
|
|
18
|
-
): Promise<{ signature: string; timestamp: number; nonce: string }> {
|
|
19
|
-
const timestamp = Math.floor(Date.now() / 1000);
|
|
20
|
-
const nonce = randomUUID();
|
|
21
|
-
const message = buildAuthMessage(action, taskId, timestamp, nonce);
|
|
22
|
-
const account = privateKeyToAccount(wallet.privateKey);
|
|
23
|
-
const signature = await account.signMessage({ message });
|
|
24
|
-
return { signature, timestamp, nonce };
|
|
25
|
-
}
|
package/src/lib/constants.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
// MANDATE: Molt's Autonomous Network for Distributed Agent Task Execution
|
|
2
|
-
// Chain: Base Mainnet
|
|
3
|
-
|
|
4
|
-
export const CHAIN_ID = 8453;
|
|
5
|
-
|
|
6
|
-
export const CONTRACTS = {
|
|
7
|
-
// ERC-8004 Registries (Base Mainnet) - https://github.com/erc-8004/erc-8004-contracts
|
|
8
|
-
IDENTITY_REGISTRY: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432" as `0x${string}`,
|
|
9
|
-
REPUTATION_REGISTRY: "0x8004BAa17C55a88189AE136b182e5fdA19dE9b63" as `0x${string}`,
|
|
10
|
-
// Note: Validation Registry not yet deployed (optional in spec)
|
|
11
|
-
|
|
12
|
-
// Flaunch (existing)
|
|
13
|
-
FLAUNCH_FACTORY: "0x6A53F8b799bE11a2A3264eF0bfF183dCB12d9571" as `0x${string}`,
|
|
14
|
-
POSITION_MANAGER: "0x51Bba15255406Cfe7099a42183302640ba7dAFDC" as `0x${string}`,
|
|
15
|
-
|
|
16
|
-
// Base
|
|
17
|
-
WETH: "0x4200000000000000000000000000000000000006" as `0x${string}`,
|
|
18
|
-
MULTICALL3: "0xcA11bde05977b3631167028862bE2a173976CA11" as `0x${string}`,
|
|
19
|
-
} as const;
|
|
20
|
-
|
|
21
|
-
// Revenue Manager - collects 10% protocol fees from token trading
|
|
22
|
-
export const REVENUE_MANAGER_ADDRESS = "0x3Bc08524d9DaaDEC9d1Af87818d809611F0fD669" as `0x${string}`;
|
|
23
|
-
|
|
24
|
-
export const APIS = {
|
|
25
|
-
FLAUNCH: "https://web2-api.flaunch.gg",
|
|
26
|
-
FLAUNCH_DATA: "https://api.flayerlabs.xyz",
|
|
27
|
-
MANDATE: "https://api.moltlaunch.com",
|
|
28
|
-
} as const;
|
|
29
|
-
|
|
30
|
-
export const CHAIN = {
|
|
31
|
-
id: 8453,
|
|
32
|
-
name: "Base",
|
|
33
|
-
network: "base",
|
|
34
|
-
explorer: "https://basescan.org",
|
|
35
|
-
flaunchUrl: "https://flaunch.gg/base",
|
|
36
|
-
} as const;
|
|
37
|
-
|
|
38
|
-
// Flaunch polling config
|
|
39
|
-
export const POLL_INTERVAL_MS = 2_000;
|
|
40
|
-
export const POLL_TIMEOUT_MS = 120_000;
|
|
41
|
-
export const MAX_IMAGE_SIZE_BYTES = 5 * 1024 * 1024; // 5MB
|
|
42
|
-
|
|
43
|
-
// RPC endpoint - use env var for reliability (Alchemy, QuickNode, etc.)
|
|
44
|
-
// Fallback to public Base RPC (may be rate limited)
|
|
45
|
-
export const BASE_RPC_URL = process.env.BASE_RPC_URL || "https://mainnet.base.org";
|
|
46
|
-
|
|
47
|
-
export const WALLET_PATH = ".moltlaunch/wallet.json";
|
|
48
|
-
|
|
49
|
-
// Agent metadata keys (stored in ERC-8004 identity registry)
|
|
50
|
-
export const METADATA_KEYS = {
|
|
51
|
-
FLAUNCH_TOKEN: "flaunchToken", // Link to Flaunch token address
|
|
52
|
-
SKILLS: "skills", // Comma-separated skill list
|
|
53
|
-
ENDPOINT: "endpoint", // x402 hire endpoint
|
|
54
|
-
PRICE_WEI: "priceWei", // Base price per hire in wei
|
|
55
|
-
} as const;
|
package/src/lib/escrow.ts
DELETED
|
@@ -1,374 +0,0 @@
|
|
|
1
|
-
// Escrow contract interactions (V4 with dispute mechanism)
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
createPublicClient,
|
|
5
|
-
createWalletClient,
|
|
6
|
-
http,
|
|
7
|
-
keccak256,
|
|
8
|
-
toBytes,
|
|
9
|
-
parseAbi,
|
|
10
|
-
} from "viem";
|
|
11
|
-
import { privateKeyToAccount } from "viem/accounts";
|
|
12
|
-
import { base } from "viem/chains";
|
|
13
|
-
import { BASE_RPC_URL } from "./constants.js";
|
|
14
|
-
import type { Wallet } from "./types.js";
|
|
15
|
-
|
|
16
|
-
export enum EscrowStatus {
|
|
17
|
-
Active = 0,
|
|
18
|
-
Submitted = 1,
|
|
19
|
-
Disputed = 2,
|
|
20
|
-
Resolved = 3,
|
|
21
|
-
Released = 4,
|
|
22
|
-
Refunded = 5,
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const ESCROW_ADDRESS = (process.env.ESCROW_ADDRESS ||
|
|
26
|
-
"0x2c46054b4577b4fcdde28cb613dc2ba4b1127b0c") as `0x${string}`;
|
|
27
|
-
|
|
28
|
-
const ESCROW_ABI = parseAbi([
|
|
29
|
-
// Write functions
|
|
30
|
-
"function deposit(bytes32 taskId, address agent, address token) external payable",
|
|
31
|
-
"function markSubmitted(bytes32 taskId) external",
|
|
32
|
-
"function release(bytes32 taskId) external",
|
|
33
|
-
"function refund(bytes32 taskId) external",
|
|
34
|
-
"function releaseAfterTimeout(bytes32 taskId) external",
|
|
35
|
-
"function dispute(bytes32 taskId) external payable",
|
|
36
|
-
"function resolveDispute(bytes32 taskId, bool clientWins) external",
|
|
37
|
-
"function setDisputeFeeBps(uint256 bps) external",
|
|
38
|
-
// View functions
|
|
39
|
-
"function isPending(bytes32 taskId) external view returns (bool)",
|
|
40
|
-
"function isTimedOut(bytes32 taskId) external view returns (bool)",
|
|
41
|
-
"function isDisputed(bytes32 taskId) external view returns (bool)",
|
|
42
|
-
"function timeUntilTimeout(bytes32 taskId) external view returns (uint256)",
|
|
43
|
-
"function getEscrow(bytes32 taskId) external view returns (address client, address agent, address token, uint256 amount, uint256 depositedAt, uint256 submittedAt, uint256 disputeFee, uint8 status)",
|
|
44
|
-
"function getDisputeFee(bytes32 taskId) external view returns (uint256)",
|
|
45
|
-
"function getStatus(bytes32 taskId) external view returns (uint8)",
|
|
46
|
-
"function disputeFeeBps() external view returns (uint256)",
|
|
47
|
-
"function TIMEOUT() external view returns (uint256)",
|
|
48
|
-
// Events
|
|
49
|
-
"event Deposited(bytes32 indexed taskId, address indexed client, address indexed token, uint256 amount)",
|
|
50
|
-
"event Submitted(bytes32 indexed taskId, address indexed agent, uint256 deadline)",
|
|
51
|
-
"event Disputed(bytes32 indexed taskId, address indexed client, uint256 disputeFee)",
|
|
52
|
-
"event DisputeResolved(bytes32 indexed taskId, bool clientWins)",
|
|
53
|
-
"event BuybackBurned(bytes32 indexed taskId, address indexed token, uint256 ethAmount)",
|
|
54
|
-
"event Refunded(bytes32 indexed taskId, address indexed client, uint256 amount)",
|
|
55
|
-
"event FallbackToAgent(bytes32 indexed taskId, address indexed agent, uint256 amount)",
|
|
56
|
-
]);
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Convert task ID string to bytes32 for contract
|
|
60
|
-
*/
|
|
61
|
-
export function taskIdToBytes32(taskId: string): `0x${string}` {
|
|
62
|
-
return keccak256(toBytes(taskId));
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function getPublicClient() {
|
|
66
|
-
return createPublicClient({
|
|
67
|
-
chain: base,
|
|
68
|
-
transport: http(BASE_RPC_URL),
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function getWalletClient(wallet: Wallet) {
|
|
73
|
-
const account = privateKeyToAccount(wallet.privateKey);
|
|
74
|
-
return createWalletClient({
|
|
75
|
-
account,
|
|
76
|
-
chain: base,
|
|
77
|
-
transport: http(BASE_RPC_URL),
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Deposit funds into escrow when client accepts a quote
|
|
83
|
-
* V3: Now includes token address for buyback-and-burn
|
|
84
|
-
*/
|
|
85
|
-
export async function depositEscrow(
|
|
86
|
-
wallet: Wallet,
|
|
87
|
-
taskId: string,
|
|
88
|
-
agentAddress: string,
|
|
89
|
-
tokenAddress: string,
|
|
90
|
-
amountWei: bigint
|
|
91
|
-
): Promise<string> {
|
|
92
|
-
const walletClient = getWalletClient(wallet);
|
|
93
|
-
const taskIdBytes = taskIdToBytes32(taskId);
|
|
94
|
-
|
|
95
|
-
const hash = await walletClient.writeContract({
|
|
96
|
-
address: ESCROW_ADDRESS,
|
|
97
|
-
abi: ESCROW_ABI,
|
|
98
|
-
functionName: "deposit",
|
|
99
|
-
args: [taskIdBytes, agentAddress as `0x${string}`, tokenAddress as `0x${string}`],
|
|
100
|
-
value: amountWei,
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
return hash;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Agent marks work as submitted (starts 24h timeout countdown)
|
|
108
|
-
*/
|
|
109
|
-
export async function markSubmitted(
|
|
110
|
-
wallet: Wallet,
|
|
111
|
-
taskId: string
|
|
112
|
-
): Promise<string> {
|
|
113
|
-
const walletClient = getWalletClient(wallet);
|
|
114
|
-
const taskIdBytes = taskIdToBytes32(taskId);
|
|
115
|
-
|
|
116
|
-
const hash = await walletClient.writeContract({
|
|
117
|
-
address: ESCROW_ADDRESS,
|
|
118
|
-
abi: ESCROW_ABI,
|
|
119
|
-
functionName: "markSubmitted",
|
|
120
|
-
args: [taskIdBytes],
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
return hash;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Release funds from escrow to agent when client approves work
|
|
128
|
-
*/
|
|
129
|
-
export async function releaseEscrow(
|
|
130
|
-
wallet: Wallet,
|
|
131
|
-
taskId: string
|
|
132
|
-
): Promise<string> {
|
|
133
|
-
const walletClient = getWalletClient(wallet);
|
|
134
|
-
const taskIdBytes = taskIdToBytes32(taskId);
|
|
135
|
-
|
|
136
|
-
const hash = await walletClient.writeContract({
|
|
137
|
-
address: ESCROW_ADDRESS,
|
|
138
|
-
abi: ESCROW_ABI,
|
|
139
|
-
functionName: "release",
|
|
140
|
-
args: [taskIdBytes],
|
|
141
|
-
gas: 1_000_000n, // Buyback swap needs ~750K gas
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
return hash;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Refund funds from escrow back to client (only before submission)
|
|
149
|
-
*/
|
|
150
|
-
export async function refundEscrow(
|
|
151
|
-
wallet: Wallet,
|
|
152
|
-
taskId: string
|
|
153
|
-
): Promise<string> {
|
|
154
|
-
const walletClient = getWalletClient(wallet);
|
|
155
|
-
const taskIdBytes = taskIdToBytes32(taskId);
|
|
156
|
-
|
|
157
|
-
const hash = await walletClient.writeContract({
|
|
158
|
-
address: ESCROW_ADDRESS,
|
|
159
|
-
abi: ESCROW_ABI,
|
|
160
|
-
functionName: "refund",
|
|
161
|
-
args: [taskIdBytes],
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
return hash;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Release funds after timeout (anyone can call, agent protection)
|
|
169
|
-
*/
|
|
170
|
-
export async function releaseAfterTimeout(
|
|
171
|
-
wallet: Wallet,
|
|
172
|
-
taskId: string
|
|
173
|
-
): Promise<string> {
|
|
174
|
-
const walletClient = getWalletClient(wallet);
|
|
175
|
-
const taskIdBytes = taskIdToBytes32(taskId);
|
|
176
|
-
|
|
177
|
-
const hash = await walletClient.writeContract({
|
|
178
|
-
address: ESCROW_ADDRESS,
|
|
179
|
-
abi: ESCROW_ABI,
|
|
180
|
-
functionName: "releaseAfterTimeout",
|
|
181
|
-
args: [taskIdBytes],
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
return hash;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Check if escrow exists and is pending for a task
|
|
189
|
-
*/
|
|
190
|
-
export async function isEscrowPending(taskId: string): Promise<boolean> {
|
|
191
|
-
const publicClient = getPublicClient();
|
|
192
|
-
const taskIdBytes = taskIdToBytes32(taskId);
|
|
193
|
-
|
|
194
|
-
const pending = await publicClient.readContract({
|
|
195
|
-
address: ESCROW_ADDRESS,
|
|
196
|
-
abi: ESCROW_ABI,
|
|
197
|
-
functionName: "isPending",
|
|
198
|
-
args: [taskIdBytes],
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
return pending as boolean;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Check if timeout has been reached
|
|
206
|
-
*/
|
|
207
|
-
export async function isEscrowTimedOut(taskId: string): Promise<boolean> {
|
|
208
|
-
const publicClient = getPublicClient();
|
|
209
|
-
const taskIdBytes = taskIdToBytes32(taskId);
|
|
210
|
-
|
|
211
|
-
const timedOut = await publicClient.readContract({
|
|
212
|
-
address: ESCROW_ADDRESS,
|
|
213
|
-
abi: ESCROW_ABI,
|
|
214
|
-
functionName: "isTimedOut",
|
|
215
|
-
args: [taskIdBytes],
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
return timedOut as boolean;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Get seconds until timeout (0 if already timed out or not submitted)
|
|
223
|
-
*/
|
|
224
|
-
export async function getTimeUntilTimeout(taskId: string): Promise<bigint> {
|
|
225
|
-
const publicClient = getPublicClient();
|
|
226
|
-
const taskIdBytes = taskIdToBytes32(taskId);
|
|
227
|
-
|
|
228
|
-
const time = await publicClient.readContract({
|
|
229
|
-
address: ESCROW_ADDRESS,
|
|
230
|
-
abi: ESCROW_ABI,
|
|
231
|
-
functionName: "timeUntilTimeout",
|
|
232
|
-
args: [taskIdBytes],
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
return time as bigint;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Get escrow details for a task
|
|
240
|
-
* V4: Includes disputeFee and status enum
|
|
241
|
-
*/
|
|
242
|
-
export async function getEscrowDetails(taskId: string): Promise<{
|
|
243
|
-
client: string;
|
|
244
|
-
agent: string;
|
|
245
|
-
token: string;
|
|
246
|
-
amount: bigint;
|
|
247
|
-
depositedAt: bigint;
|
|
248
|
-
submittedAt: bigint;
|
|
249
|
-
disputeFee: bigint;
|
|
250
|
-
status: EscrowStatus;
|
|
251
|
-
} | null> {
|
|
252
|
-
const publicClient = getPublicClient();
|
|
253
|
-
const taskIdBytes = taskIdToBytes32(taskId);
|
|
254
|
-
|
|
255
|
-
const result = await publicClient.readContract({
|
|
256
|
-
address: ESCROW_ADDRESS,
|
|
257
|
-
abi: ESCROW_ABI,
|
|
258
|
-
functionName: "getEscrow",
|
|
259
|
-
args: [taskIdBytes],
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
const [client, agent, token, amount, depositedAt, submittedAt, disputeFee, status] = result as [
|
|
263
|
-
string,
|
|
264
|
-
string,
|
|
265
|
-
string,
|
|
266
|
-
bigint,
|
|
267
|
-
bigint,
|
|
268
|
-
bigint,
|
|
269
|
-
bigint,
|
|
270
|
-
number
|
|
271
|
-
];
|
|
272
|
-
|
|
273
|
-
if (amount === BigInt(0)) return null;
|
|
274
|
-
|
|
275
|
-
return { client, agent, token, amount, depositedAt, submittedAt, disputeFee, status: status as EscrowStatus };
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Client disputes submitted work (must pay fee)
|
|
280
|
-
*/
|
|
281
|
-
export async function disputeEscrow(
|
|
282
|
-
wallet: Wallet,
|
|
283
|
-
taskId: string,
|
|
284
|
-
feeWei: bigint
|
|
285
|
-
): Promise<string> {
|
|
286
|
-
const walletClient = getWalletClient(wallet);
|
|
287
|
-
const taskIdBytes = taskIdToBytes32(taskId);
|
|
288
|
-
|
|
289
|
-
const hash = await walletClient.writeContract({
|
|
290
|
-
address: ESCROW_ADDRESS,
|
|
291
|
-
abi: ESCROW_ABI,
|
|
292
|
-
functionName: "dispute",
|
|
293
|
-
args: [taskIdBytes],
|
|
294
|
-
value: feeWei,
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
return hash;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Get the required dispute fee for a task
|
|
302
|
-
*/
|
|
303
|
-
export async function getDisputeFee(taskId: string): Promise<bigint> {
|
|
304
|
-
const publicClient = getPublicClient();
|
|
305
|
-
const taskIdBytes = taskIdToBytes32(taskId);
|
|
306
|
-
|
|
307
|
-
const fee = await publicClient.readContract({
|
|
308
|
-
address: ESCROW_ADDRESS,
|
|
309
|
-
abi: ESCROW_ABI,
|
|
310
|
-
functionName: "getDisputeFee",
|
|
311
|
-
args: [taskIdBytes],
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
return fee as bigint;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Check if escrow is in disputed state
|
|
319
|
-
*/
|
|
320
|
-
export async function isEscrowDisputed(taskId: string): Promise<boolean> {
|
|
321
|
-
const publicClient = getPublicClient();
|
|
322
|
-
const taskIdBytes = taskIdToBytes32(taskId);
|
|
323
|
-
|
|
324
|
-
const disputed = await publicClient.readContract({
|
|
325
|
-
address: ESCROW_ADDRESS,
|
|
326
|
-
abi: ESCROW_ABI,
|
|
327
|
-
functionName: "isDisputed",
|
|
328
|
-
args: [taskIdBytes],
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
return disputed as boolean;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Get current escrow status
|
|
336
|
-
*/
|
|
337
|
-
export async function getEscrowStatus(taskId: string): Promise<EscrowStatus> {
|
|
338
|
-
const publicClient = getPublicClient();
|
|
339
|
-
const taskIdBytes = taskIdToBytes32(taskId);
|
|
340
|
-
|
|
341
|
-
const status = await publicClient.readContract({
|
|
342
|
-
address: ESCROW_ADDRESS,
|
|
343
|
-
abi: ESCROW_ABI,
|
|
344
|
-
functionName: "getStatus",
|
|
345
|
-
args: [taskIdBytes],
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
return (status as number) as EscrowStatus;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
/**
|
|
352
|
-
* Admin resolves a disputed escrow
|
|
353
|
-
*/
|
|
354
|
-
export async function resolveEscrowDispute(
|
|
355
|
-
wallet: Wallet,
|
|
356
|
-
taskId: string,
|
|
357
|
-
clientWins: boolean,
|
|
358
|
-
): Promise<string> {
|
|
359
|
-
const walletClient = getWalletClient(wallet);
|
|
360
|
-
const publicClient = getPublicClient();
|
|
361
|
-
const taskIdBytes = taskIdToBytes32(taskId);
|
|
362
|
-
|
|
363
|
-
const hash = await walletClient.writeContract({
|
|
364
|
-
address: ESCROW_ADDRESS,
|
|
365
|
-
abi: ESCROW_ABI,
|
|
366
|
-
functionName: "resolveDispute",
|
|
367
|
-
args: [taskIdBytes, clientWins],
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
await publicClient.waitForTransactionReceipt({ hash });
|
|
371
|
-
return hash;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
export { ESCROW_ADDRESS };
|
package/src/lib/files.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
// CLI helpers for uploading/downloading task files via Worker proxy
|
|
2
|
-
|
|
3
|
-
import { readFileSync, statSync } from "fs";
|
|
4
|
-
import { basename } from "path";
|
|
5
|
-
import { APIS } from "./constants.js";
|
|
6
|
-
import { signAction } from "./auth.js";
|
|
7
|
-
import type { Wallet, TaskFile } from "./types.js";
|
|
8
|
-
|
|
9
|
-
const API_BASE = APIS.MANDATE;
|
|
10
|
-
|
|
11
|
-
/** Upload a single file to the task's R2 storage */
|
|
12
|
-
export async function uploadFile(
|
|
13
|
-
wallet: Wallet,
|
|
14
|
-
taskId: string,
|
|
15
|
-
filePath: string,
|
|
16
|
-
): Promise<TaskFile> {
|
|
17
|
-
const { signature, timestamp, nonce } = await signAction(wallet, "upload", taskId);
|
|
18
|
-
const filename = basename(filePath);
|
|
19
|
-
const stat = statSync(filePath);
|
|
20
|
-
const body = readFileSync(filePath);
|
|
21
|
-
|
|
22
|
-
const params = new URLSearchParams({
|
|
23
|
-
signature,
|
|
24
|
-
timestamp: String(timestamp),
|
|
25
|
-
nonce,
|
|
26
|
-
filename,
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
const response = await fetch(
|
|
30
|
-
`${API_BASE}/api/tasks/${taskId}/upload?${params.toString()}`,
|
|
31
|
-
{
|
|
32
|
-
method: "POST",
|
|
33
|
-
headers: {
|
|
34
|
-
"Content-Type": "application/octet-stream",
|
|
35
|
-
"Content-Length": String(stat.size),
|
|
36
|
-
},
|
|
37
|
-
body,
|
|
38
|
-
},
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
if (!response.ok) {
|
|
42
|
-
const error = (await response.json()) as { error: string };
|
|
43
|
-
throw new Error(error.error || `Upload failed: HTTP ${response.status}`);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const data = (await response.json()) as { file: TaskFile };
|
|
47
|
-
return data.file;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** Upload multiple files, return all file metadata */
|
|
51
|
-
export async function uploadFiles(
|
|
52
|
-
wallet: Wallet,
|
|
53
|
-
taskId: string,
|
|
54
|
-
filePaths: string[],
|
|
55
|
-
): Promise<TaskFile[]> {
|
|
56
|
-
const results: TaskFile[] = [];
|
|
57
|
-
for (const filePath of filePaths) {
|
|
58
|
-
const file = await uploadFile(wallet, taskId, filePath);
|
|
59
|
-
results.push(file);
|
|
60
|
-
}
|
|
61
|
-
return results;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** Download a file from a task's R2 storage */
|
|
65
|
-
export async function downloadTaskFile(
|
|
66
|
-
taskId: string,
|
|
67
|
-
fileKey: string,
|
|
68
|
-
): Promise<{ data: ArrayBuffer; contentType: string; filename: string }> {
|
|
69
|
-
const response = await fetch(
|
|
70
|
-
`${API_BASE}/api/tasks/${taskId}/files/${encodeURIComponent(fileKey)}`,
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
if (!response.ok) {
|
|
74
|
-
const error = (await response.json()) as { error: string };
|
|
75
|
-
throw new Error(error.error || `Download failed: HTTP ${response.status}`);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const disposition = response.headers.get("Content-Disposition") || "";
|
|
79
|
-
const filenameMatch = disposition.match(/filename="(.+)"/);
|
|
80
|
-
const filename = filenameMatch ? filenameMatch[1] : "file";
|
|
81
|
-
|
|
82
|
-
return {
|
|
83
|
-
data: await response.arrayBuffer(),
|
|
84
|
-
contentType: response.headers.get("Content-Type") || "application/octet-stream",
|
|
85
|
-
filename,
|
|
86
|
-
};
|
|
87
|
-
}
|