@sherwoodagent/cli 0.84.0 → 0.85.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.
- package/dist/{chat-UZ3BBEXV.js → chat-DNWTI4CZ.js} +5 -5
- package/dist/{chunk-56YYNNNQ.js → chunk-53W6CYYV.js} +2 -2
- package/dist/{chunk-JUVXLTL2.js → chunk-65G5BFYR.js} +2 -2
- package/dist/{chunk-BOC7VB5B.js → chunk-6N7EVSAZ.js} +2 -2
- package/dist/{chunk-DNRIKCJ5.js → chunk-AM6MI2JF.js} +6 -10
- package/dist/{chunk-DNRIKCJ5.js.map → chunk-AM6MI2JF.js.map} +1 -1
- package/dist/{chunk-6DRNSZ2Z.js → chunk-ARSQ5UQA.js} +2 -2
- package/dist/{chunk-MQ55QOGD.js → chunk-I2BBOJNC.js} +2 -2
- package/dist/chunk-ILHOW6IA.js +2971 -0
- package/dist/chunk-ILHOW6IA.js.map +1 -0
- package/dist/{chunk-NCFN66ZH.js → chunk-M4GX5PL2.js} +5 -3
- package/dist/chunk-M4GX5PL2.js.map +1 -0
- package/dist/{chunk-SNR5R5TO.js → chunk-TWT56FTL.js} +3 -3
- package/dist/{eas-QPKX6A5A.js → eas-D3BVHBJH.js} +5 -3
- package/dist/{governor-7QKGNHMR.js → governor-35IXAEDJ.js} +3 -3
- package/dist/identity-RMEPBFOL.js +15 -0
- package/dist/index.js +2553 -5400
- package/dist/index.js.map +1 -1
- package/dist/{price-SVF25X4O.js → price-I5T77X7Y.js} +3 -3
- package/dist/{research-YSNJ5HTM.js → research-GZNVG3SP.js} +3 -3
- package/dist/{session-RMAIFRS6.js → session-M5QB25PK.js} +8 -8
- package/dist/{trade-YPLTIS56.js → trade-TCQ5NUNA.js} +6 -6
- package/dist/{vault-UCFFOOTS.js → vault-K2R4VHVI.js} +5 -5
- package/dist/{xmtp-S7IGTIMZ.js → xmtp-R25Q5GWY.js} +3 -3
- package/package.json +1 -1
- package/dist/addresses-LFYVOPOH.js +0 -45
- package/dist/chunk-NCFN66ZH.js.map +0 -1
- /package/dist/{chat-UZ3BBEXV.js.map → chat-DNWTI4CZ.js.map} +0 -0
- /package/dist/{chunk-56YYNNNQ.js.map → chunk-53W6CYYV.js.map} +0 -0
- /package/dist/{chunk-JUVXLTL2.js.map → chunk-65G5BFYR.js.map} +0 -0
- /package/dist/{chunk-BOC7VB5B.js.map → chunk-6N7EVSAZ.js.map} +0 -0
- /package/dist/{chunk-6DRNSZ2Z.js.map → chunk-ARSQ5UQA.js.map} +0 -0
- /package/dist/{chunk-MQ55QOGD.js.map → chunk-I2BBOJNC.js.map} +0 -0
- /package/dist/{chunk-SNR5R5TO.js.map → chunk-TWT56FTL.js.map} +0 -0
- /package/dist/{addresses-LFYVOPOH.js.map → eas-D3BVHBJH.js.map} +0 -0
- /package/dist/{eas-QPKX6A5A.js.map → governor-35IXAEDJ.js.map} +0 -0
- /package/dist/{governor-7QKGNHMR.js.map → identity-RMEPBFOL.js.map} +0 -0
- /package/dist/{price-SVF25X4O.js.map → price-I5T77X7Y.js.map} +0 -0
- /package/dist/{research-YSNJ5HTM.js.map → research-GZNVG3SP.js.map} +0 -0
- /package/dist/{session-RMAIFRS6.js.map → session-M5QB25PK.js.map} +0 -0
- /package/dist/{trade-YPLTIS56.js.map → trade-TCQ5NUNA.js.map} +0 -0
- /package/dist/{vault-UCFFOOTS.js.map → vault-K2R4VHVI.js.map} +0 -0
- /package/dist/{xmtp-S7IGTIMZ.js.map → xmtp-R25Q5GWY.js.map} +0 -0
|
@@ -0,0 +1,2971 @@
|
|
|
1
|
+
import {
|
|
2
|
+
COORDINATION_NETWORK
|
|
3
|
+
} from "./chunk-M4GX5PL2.js";
|
|
4
|
+
import {
|
|
5
|
+
AGENT_REGISTRY
|
|
6
|
+
} from "./chunk-AM6MI2JF.js";
|
|
7
|
+
import {
|
|
8
|
+
formatContractError,
|
|
9
|
+
getAccount,
|
|
10
|
+
getPublicClient,
|
|
11
|
+
sendTxWithRetry,
|
|
12
|
+
waitForReceipt
|
|
13
|
+
} from "./chunk-QRMZRAMG.js";
|
|
14
|
+
import {
|
|
15
|
+
getChain,
|
|
16
|
+
getChainConfig,
|
|
17
|
+
getRpcUrl,
|
|
18
|
+
withNetwork
|
|
19
|
+
} from "./chunk-PVTZMRC6.js";
|
|
20
|
+
import {
|
|
21
|
+
getAgentId,
|
|
22
|
+
getIdentityChainId,
|
|
23
|
+
getIdentityProvider,
|
|
24
|
+
getVirtualsIdentity,
|
|
25
|
+
requirePrivateKey,
|
|
26
|
+
setAgentId,
|
|
27
|
+
setVirtualsIdentity
|
|
28
|
+
} from "./chunk-3TZIOAPH.js";
|
|
29
|
+
|
|
30
|
+
// src/commands/identity.ts
|
|
31
|
+
import { generatePrivateKey } from "viem/accounts";
|
|
32
|
+
import chalk from "chalk";
|
|
33
|
+
import ora from "ora";
|
|
34
|
+
import { SDK } from "agent0-sdk";
|
|
35
|
+
|
|
36
|
+
// src/lib/virtuals.ts
|
|
37
|
+
import { execFile } from "child_process";
|
|
38
|
+
import { promisify } from "util";
|
|
39
|
+
import { createPublicClient, http, recoverMessageAddress, isAddress } from "viem";
|
|
40
|
+
import { base, baseSepolia } from "viem/chains";
|
|
41
|
+
var execFileAsync = promisify(execFile);
|
|
42
|
+
var BINDING_VERSION = "v1";
|
|
43
|
+
function buildBindingMessage(sherwoodEoa, issuingChainId, tokenId) {
|
|
44
|
+
return `sherwood-identity-link:${BINDING_VERSION}:${sherwoodEoa.toLowerCase()}:${issuingChainId}:${tokenId}`;
|
|
45
|
+
}
|
|
46
|
+
async function verifyBinding(params) {
|
|
47
|
+
try {
|
|
48
|
+
const recovered = await recoverMessageAddress({
|
|
49
|
+
message: buildBindingMessage(params.sherwoodEoa, params.issuingChainId, params.tokenId),
|
|
50
|
+
signature: params.signature
|
|
51
|
+
});
|
|
52
|
+
return recovered.toLowerCase() === params.virtualsWallet.toLowerCase();
|
|
53
|
+
} catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
var robinhoodChain = {
|
|
58
|
+
id: 4663,
|
|
59
|
+
name: "Robinhood Chain",
|
|
60
|
+
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
|
|
61
|
+
rpcUrls: { default: { http: ["https://rpc.mainnet.chain.robinhood.com"] } },
|
|
62
|
+
blockExplorers: {
|
|
63
|
+
default: {
|
|
64
|
+
name: "Robinhood Chain Explorer",
|
|
65
|
+
url: "https://explorer.mainnet.chain.robinhood.com"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
var DEFAULT_ISSUING_CHAIN_ID = robinhoodChain.id;
|
|
70
|
+
var ISSUING_CHAINS = {
|
|
71
|
+
[robinhoodChain.id]: {
|
|
72
|
+
chain: robinhoodChain,
|
|
73
|
+
identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432"
|
|
74
|
+
},
|
|
75
|
+
[base.id]: {
|
|
76
|
+
chain: base,
|
|
77
|
+
identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432"
|
|
78
|
+
},
|
|
79
|
+
[baseSepolia.id]: {
|
|
80
|
+
chain: baseSepolia,
|
|
81
|
+
identityRegistry: "0x8004A818BFB912233c491871b3d84c89A494BD9e"
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
var OWNER_OF_ABI = [
|
|
85
|
+
{
|
|
86
|
+
name: "ownerOf",
|
|
87
|
+
type: "function",
|
|
88
|
+
stateMutability: "view",
|
|
89
|
+
inputs: [{ name: "tokenId", type: "uint256" }],
|
|
90
|
+
outputs: [{ name: "", type: "address" }]
|
|
91
|
+
}
|
|
92
|
+
];
|
|
93
|
+
var ISSUING_RPC_ENV = {
|
|
94
|
+
[robinhoodChain.id]: "ROBINHOOD_RPC_URL",
|
|
95
|
+
[base.id]: "BASE_RPC_URL",
|
|
96
|
+
[baseSepolia.id]: "BASE_SEPOLIA_RPC_URL"
|
|
97
|
+
};
|
|
98
|
+
async function ownerOfOnIssuingChain(issuingChainId, tokenId) {
|
|
99
|
+
const issuing = ISSUING_CHAINS[issuingChainId];
|
|
100
|
+
if (!issuing) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
`Unsupported issuing chain ${issuingChainId}. Supported: ${Object.keys(ISSUING_CHAINS).join(", ")}`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
const rpcOverride = process.env[ISSUING_RPC_ENV[issuingChainId]];
|
|
106
|
+
const client = createPublicClient({ chain: issuing.chain, transport: http(rpcOverride) });
|
|
107
|
+
return client.readContract({
|
|
108
|
+
address: issuing.identityRegistry,
|
|
109
|
+
abi: OWNER_OF_ABI,
|
|
110
|
+
functionName: "ownerOf",
|
|
111
|
+
args: [tokenId]
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
var WALLET_ONLY_CHAIN_ID = 0;
|
|
115
|
+
var VID_TAG_RE = /\s*\[vid:(v\d+):(\d+):(\d+):(0x[0-9a-fA-F]{40}):(self|0x[0-9a-fA-F]{130})\]\s*$/;
|
|
116
|
+
function formatVidTag(tag) {
|
|
117
|
+
return `[vid:${BINDING_VERSION}:${tag.issuingChainId}:${tag.tokenId}:${tag.wallet.toLowerCase()}:${tag.signature}]`;
|
|
118
|
+
}
|
|
119
|
+
function appendVidTag(message, tag) {
|
|
120
|
+
return `${message} ${formatVidTag(tag)}`;
|
|
121
|
+
}
|
|
122
|
+
function parseVidTag(message) {
|
|
123
|
+
if (message.indexOf("[vid:") !== message.lastIndexOf("[vid:")) return null;
|
|
124
|
+
const match = message.match(VID_TAG_RE);
|
|
125
|
+
if (!match) return null;
|
|
126
|
+
if (!isAddress(match[4])) return null;
|
|
127
|
+
return {
|
|
128
|
+
version: match[1],
|
|
129
|
+
issuingChainId: parseInt(match[2], 10),
|
|
130
|
+
tokenId: BigInt(match[3]),
|
|
131
|
+
wallet: match[4],
|
|
132
|
+
signature: match[5] === "self" ? "self" : match[5]
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function stripVidTag(message) {
|
|
136
|
+
return message.replace(VID_TAG_RE, "");
|
|
137
|
+
}
|
|
138
|
+
async function verifyVidTag(tag, attester, ownerOf = ownerOfOnIssuingChain) {
|
|
139
|
+
let bindingOk;
|
|
140
|
+
if (tag.signature === "self") {
|
|
141
|
+
bindingOk = attester.toLowerCase() === tag.wallet.toLowerCase();
|
|
142
|
+
} else {
|
|
143
|
+
bindingOk = await verifyBinding({
|
|
144
|
+
sherwoodEoa: attester,
|
|
145
|
+
issuingChainId: tag.issuingChainId,
|
|
146
|
+
tokenId: tag.tokenId,
|
|
147
|
+
virtualsWallet: tag.wallet,
|
|
148
|
+
signature: tag.signature
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
if (!bindingOk) {
|
|
152
|
+
return { bindingOk: false, ownerOk: false, error: "binding failed \u2014 ownership check skipped" };
|
|
153
|
+
}
|
|
154
|
+
if (tag.issuingChainId === WALLET_ONLY_CHAIN_ID) {
|
|
155
|
+
return { bindingOk, ownerOk: true, walletOnly: true };
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
const owner = await ownerOf(tag.issuingChainId, tag.tokenId);
|
|
159
|
+
return {
|
|
160
|
+
bindingOk,
|
|
161
|
+
ownerOk: owner.toLowerCase() === tag.wallet.toLowerCase(),
|
|
162
|
+
owner
|
|
163
|
+
};
|
|
164
|
+
} catch (err) {
|
|
165
|
+
return { bindingOk, ownerOk: false, error: err.message };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function makeOwnerOfCache() {
|
|
169
|
+
const cache = /* @__PURE__ */ new Map();
|
|
170
|
+
return (chainId, tokenId) => {
|
|
171
|
+
const key = `${chainId}:${tokenId}`;
|
|
172
|
+
let p = cache.get(key);
|
|
173
|
+
if (!p) {
|
|
174
|
+
p = ownerOfOnIssuingChain(chainId, tokenId);
|
|
175
|
+
p.catch(() => {
|
|
176
|
+
});
|
|
177
|
+
cache.set(key, p);
|
|
178
|
+
}
|
|
179
|
+
return p;
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
async function acpJson(args) {
|
|
183
|
+
try {
|
|
184
|
+
const { stdout } = await execFileAsync("acp", [...args, "--json"], {
|
|
185
|
+
timeout: 15e3
|
|
186
|
+
});
|
|
187
|
+
const start = stdout.indexOf("{");
|
|
188
|
+
if (start < 0) return null;
|
|
189
|
+
return JSON.parse(stdout.slice(start));
|
|
190
|
+
} catch {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
async function detectAcp() {
|
|
195
|
+
try {
|
|
196
|
+
await execFileAsync("acp", ["--version"], { timeout: 1e4 });
|
|
197
|
+
return true;
|
|
198
|
+
} catch {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
async function acpSignMessage(message) {
|
|
203
|
+
try {
|
|
204
|
+
const { stdout } = await execFileAsync(
|
|
205
|
+
"acp",
|
|
206
|
+
["wallet", "sign-message", "--message", message, "--json"],
|
|
207
|
+
{ timeout: 6e4 }
|
|
208
|
+
);
|
|
209
|
+
const start = stdout.indexOf("{");
|
|
210
|
+
if (start < 0) return null;
|
|
211
|
+
const json = JSON.parse(stdout.slice(start));
|
|
212
|
+
const sig = json.signature;
|
|
213
|
+
return typeof sig === "string" && /^0x[0-9a-fA-F]{130}$/.test(sig) ? sig : null;
|
|
214
|
+
} catch {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
async function acpWhoami() {
|
|
219
|
+
const json = await acpJson(["agent", "whoami"]);
|
|
220
|
+
if (!json) return null;
|
|
221
|
+
const agentId = json.agentId ?? json.id ?? json.agent?.id;
|
|
222
|
+
const name = json.name ?? json.agent?.name;
|
|
223
|
+
return {
|
|
224
|
+
agentId: agentId != null ? String(agentId) : void 0,
|
|
225
|
+
name: name != null ? String(name) : void 0
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
async function acpWalletAddress() {
|
|
229
|
+
const json = await acpJson(["wallet", "address"]);
|
|
230
|
+
if (!json) return null;
|
|
231
|
+
for (const key of ["address", "evmAddress", "evm", "wallet"]) {
|
|
232
|
+
const value = json[key];
|
|
233
|
+
if (typeof value === "string" && isAddress(value)) return value;
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// src/lib/calldata.ts
|
|
239
|
+
var _calldataOnly = false;
|
|
240
|
+
function setCalldataOnly(value) {
|
|
241
|
+
_calldataOnly = value;
|
|
242
|
+
}
|
|
243
|
+
function isCalldataOnly() {
|
|
244
|
+
return _calldataOnly;
|
|
245
|
+
}
|
|
246
|
+
function emitCalldata(action) {
|
|
247
|
+
process.stdout.write(JSON.stringify(action, null, 2) + "\n");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ../sdk/dist/generated/state-machines.js
|
|
251
|
+
var GUARDIAN_VOTE_TYPES = [
|
|
252
|
+
"None",
|
|
253
|
+
"Approve",
|
|
254
|
+
"Block"
|
|
255
|
+
];
|
|
256
|
+
|
|
257
|
+
// ../sdk/dist/types.js
|
|
258
|
+
var CHAIN_IDS = {
|
|
259
|
+
BASE: 8453,
|
|
260
|
+
HYPEREVM: 999,
|
|
261
|
+
ROBINHOOD: 4663,
|
|
262
|
+
ROBINHOOD_L2: 46630,
|
|
263
|
+
// Tenderly fork of Robinhood mainnet (USDG + Uniswap v3+v4) — mainnet-faithful
|
|
264
|
+
// e2e + guardian-sim environment. Ephemeral vnet; addresses in addresses.ts.
|
|
265
|
+
ROBINHOOD_FORK: 9994663
|
|
266
|
+
};
|
|
267
|
+
var SUPPORTED_CHAIN_IDS = [
|
|
268
|
+
CHAIN_IDS.BASE,
|
|
269
|
+
CHAIN_IDS.HYPEREVM,
|
|
270
|
+
CHAIN_IDS.ROBINHOOD,
|
|
271
|
+
CHAIN_IDS.ROBINHOOD_L2,
|
|
272
|
+
CHAIN_IDS.ROBINHOOD_FORK
|
|
273
|
+
];
|
|
274
|
+
|
|
275
|
+
// ../sdk/dist/errors.js
|
|
276
|
+
var SdkError = class extends Error {
|
|
277
|
+
code;
|
|
278
|
+
cause;
|
|
279
|
+
constructor(code, message, cause) {
|
|
280
|
+
super(message);
|
|
281
|
+
this.code = code;
|
|
282
|
+
this.cause = cause;
|
|
283
|
+
this.name = "SdkError";
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
var usage = (message) => new SdkError("USAGE", message);
|
|
287
|
+
var unsupported = (message) => new SdkError("UNSUPPORTED", message);
|
|
288
|
+
var unavailable = (message, cause) => new SdkError("UNAVAILABLE", message, cause);
|
|
289
|
+
|
|
290
|
+
// ../sdk/dist/abis.js
|
|
291
|
+
var SYNDICATE_VAULT_ABI = [
|
|
292
|
+
// ERC-4626
|
|
293
|
+
{
|
|
294
|
+
name: "deposit",
|
|
295
|
+
type: "function",
|
|
296
|
+
stateMutability: "nonpayable",
|
|
297
|
+
inputs: [
|
|
298
|
+
{ name: "assets", type: "uint256" },
|
|
299
|
+
{ name: "receiver", type: "address" }
|
|
300
|
+
],
|
|
301
|
+
outputs: [{ name: "shares", type: "uint256" }]
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
name: "redeem",
|
|
305
|
+
type: "function",
|
|
306
|
+
stateMutability: "nonpayable",
|
|
307
|
+
inputs: [
|
|
308
|
+
{ name: "shares", type: "uint256" },
|
|
309
|
+
{ name: "receiver", type: "address" },
|
|
310
|
+
{ name: "owner", type: "address" }
|
|
311
|
+
],
|
|
312
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
name: "requestRedeem",
|
|
316
|
+
type: "function",
|
|
317
|
+
stateMutability: "nonpayable",
|
|
318
|
+
inputs: [
|
|
319
|
+
{ name: "shares", type: "uint256" },
|
|
320
|
+
{ name: "owner", type: "address" }
|
|
321
|
+
],
|
|
322
|
+
outputs: [{ name: "requestId", type: "uint256" }]
|
|
323
|
+
},
|
|
324
|
+
// Reads
|
|
325
|
+
{
|
|
326
|
+
name: "asset",
|
|
327
|
+
type: "function",
|
|
328
|
+
stateMutability: "view",
|
|
329
|
+
inputs: [],
|
|
330
|
+
outputs: [{ name: "", type: "address" }]
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
name: "totalAssets",
|
|
334
|
+
type: "function",
|
|
335
|
+
stateMutability: "view",
|
|
336
|
+
inputs: [],
|
|
337
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
name: "totalSupply",
|
|
341
|
+
type: "function",
|
|
342
|
+
stateMutability: "view",
|
|
343
|
+
inputs: [],
|
|
344
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
name: "decimals",
|
|
348
|
+
type: "function",
|
|
349
|
+
stateMutability: "view",
|
|
350
|
+
inputs: [],
|
|
351
|
+
outputs: [{ name: "", type: "uint8" }]
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
name: "balanceOf",
|
|
355
|
+
type: "function",
|
|
356
|
+
stateMutability: "view",
|
|
357
|
+
inputs: [{ name: "account", type: "address" }],
|
|
358
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
name: "owner",
|
|
362
|
+
type: "function",
|
|
363
|
+
stateMutability: "view",
|
|
364
|
+
inputs: [],
|
|
365
|
+
outputs: [{ name: "", type: "address" }]
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
name: "governor",
|
|
369
|
+
type: "function",
|
|
370
|
+
stateMutability: "view",
|
|
371
|
+
inputs: [],
|
|
372
|
+
outputs: [{ name: "", type: "address" }]
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
name: "redemptionsLocked",
|
|
376
|
+
type: "function",
|
|
377
|
+
stateMutability: "view",
|
|
378
|
+
inputs: [],
|
|
379
|
+
outputs: [{ name: "", type: "bool" }]
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
name: "paused",
|
|
383
|
+
type: "function",
|
|
384
|
+
stateMutability: "view",
|
|
385
|
+
inputs: [],
|
|
386
|
+
outputs: [{ name: "", type: "bool" }]
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
name: "openDeposits",
|
|
390
|
+
type: "function",
|
|
391
|
+
stateMutability: "view",
|
|
392
|
+
inputs: [],
|
|
393
|
+
outputs: [{ name: "", type: "bool" }]
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
name: "agentFeeBps",
|
|
397
|
+
type: "function",
|
|
398
|
+
stateMutability: "view",
|
|
399
|
+
inputs: [],
|
|
400
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
name: "setAgentFeeBps",
|
|
404
|
+
type: "function",
|
|
405
|
+
stateMutability: "nonpayable",
|
|
406
|
+
inputs: [{ name: "bps", type: "uint256" }],
|
|
407
|
+
outputs: []
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
name: "AgentFeeUpdated",
|
|
411
|
+
type: "event",
|
|
412
|
+
inputs: [{ name: "bps", type: "uint256", indexed: false }]
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
name: "getAgentCount",
|
|
416
|
+
type: "function",
|
|
417
|
+
stateMutability: "view",
|
|
418
|
+
inputs: [],
|
|
419
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
name: "isApprovedDepositor",
|
|
423
|
+
type: "function",
|
|
424
|
+
stateMutability: "view",
|
|
425
|
+
inputs: [{ name: "depositor", type: "address" }],
|
|
426
|
+
outputs: [{ name: "", type: "bool" }]
|
|
427
|
+
},
|
|
428
|
+
// Owner-side write surface
|
|
429
|
+
{
|
|
430
|
+
name: "approveDepositor",
|
|
431
|
+
type: "function",
|
|
432
|
+
stateMutability: "nonpayable",
|
|
433
|
+
inputs: [{ name: "depositor", type: "address" }],
|
|
434
|
+
outputs: []
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
name: "removeDepositor",
|
|
438
|
+
type: "function",
|
|
439
|
+
stateMutability: "nonpayable",
|
|
440
|
+
inputs: [{ name: "depositor", type: "address" }],
|
|
441
|
+
outputs: []
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
name: "registerAgent",
|
|
445
|
+
type: "function",
|
|
446
|
+
stateMutability: "nonpayable",
|
|
447
|
+
inputs: [
|
|
448
|
+
{ name: "agentAddress", type: "address" },
|
|
449
|
+
{ name: "agentId", type: "uint256" }
|
|
450
|
+
],
|
|
451
|
+
outputs: []
|
|
452
|
+
},
|
|
453
|
+
// Governor-only: mint + fund + run a single-use CallSandbox.
|
|
454
|
+
// ICallSandbox.Call is TWO fields (address,bytes). There is NO `value`.
|
|
455
|
+
{
|
|
456
|
+
name: "runSandbox",
|
|
457
|
+
type: "function",
|
|
458
|
+
stateMutability: "nonpayable",
|
|
459
|
+
inputs: [
|
|
460
|
+
{ name: "pid", type: "uint256" },
|
|
461
|
+
{
|
|
462
|
+
name: "calls",
|
|
463
|
+
type: "tuple[]",
|
|
464
|
+
components: [
|
|
465
|
+
{ name: "target", type: "address" },
|
|
466
|
+
{ name: "data", type: "bytes" }
|
|
467
|
+
]
|
|
468
|
+
},
|
|
469
|
+
{ name: "declaredTokens", type: "address[]" },
|
|
470
|
+
{ name: "funding", type: "uint256" }
|
|
471
|
+
],
|
|
472
|
+
outputs: [{ name: "sandbox", type: "address" }]
|
|
473
|
+
}
|
|
474
|
+
];
|
|
475
|
+
var SYNDICATE_GOVERNOR_ABI = [
|
|
476
|
+
// Propose (multi-arg). Order MUST match contract: vault, strategy,
|
|
477
|
+
// metadataURI, strategyDuration, executeCalls, settlementCalls, coProposers.
|
|
478
|
+
// Agent fee is no longer a propose() arg — it's the vault's `agentFeeBps`
|
|
479
|
+
// (owner-set), snapshotted onto the proposal at propose time and clamped to
|
|
480
|
+
// maxPerformanceFeeBps at settlement.
|
|
481
|
+
{
|
|
482
|
+
name: "propose",
|
|
483
|
+
type: "function",
|
|
484
|
+
stateMutability: "nonpayable",
|
|
485
|
+
inputs: [
|
|
486
|
+
{ name: "vault", type: "address" },
|
|
487
|
+
{ name: "strategy", type: "address" },
|
|
488
|
+
{ name: "metadataURI", type: "string" },
|
|
489
|
+
{ name: "strategyDuration", type: "uint256" },
|
|
490
|
+
// The RISK ENVELOPE the proposal commits to: the ceiling the execute
|
|
491
|
+
// batch is metered against, and the declared loss allowance the
|
|
492
|
+
// settle-price guard sizes its tolerance from.
|
|
493
|
+
{
|
|
494
|
+
name: "envelope",
|
|
495
|
+
type: "tuple",
|
|
496
|
+
components: [
|
|
497
|
+
{ name: "maxCapital", type: "uint256" },
|
|
498
|
+
{ name: "maxDrawdownBps", type: "uint16" }
|
|
499
|
+
]
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
name: "executeCalls",
|
|
503
|
+
type: "tuple[]",
|
|
504
|
+
components: [
|
|
505
|
+
{ name: "target", type: "address" },
|
|
506
|
+
{ name: "data", type: "bytes" },
|
|
507
|
+
{ name: "value", type: "uint256" }
|
|
508
|
+
]
|
|
509
|
+
},
|
|
510
|
+
// One cap per call, in ASSET UNITS — the maximum gross outflow measured
|
|
511
|
+
// around that call. Summed per batch, required <= maxCapital.
|
|
512
|
+
{ name: "executeCallCaps", type: "uint256[]" },
|
|
513
|
+
{
|
|
514
|
+
name: "settlementCalls",
|
|
515
|
+
type: "tuple[]",
|
|
516
|
+
components: [
|
|
517
|
+
{ name: "target", type: "address" },
|
|
518
|
+
{ name: "data", type: "bytes" },
|
|
519
|
+
{ name: "value", type: "uint256" }
|
|
520
|
+
]
|
|
521
|
+
},
|
|
522
|
+
{ name: "settlementCallCaps", type: "uint256[]" },
|
|
523
|
+
{
|
|
524
|
+
name: "coProposers",
|
|
525
|
+
type: "tuple[]",
|
|
526
|
+
components: [
|
|
527
|
+
{ name: "agent", type: "address" },
|
|
528
|
+
{ name: "splitBps", type: "uint256" }
|
|
529
|
+
]
|
|
530
|
+
}
|
|
531
|
+
],
|
|
532
|
+
outputs: [{ name: "proposalId", type: "uint256" }]
|
|
533
|
+
},
|
|
534
|
+
// proposeWithSandbox — same 10 args as propose, prefixed with SandboxPayload.
|
|
535
|
+
// ICallSandbox.Call is TWO fields (address,bytes). There is NO `value`:
|
|
536
|
+
// native value would open a transfer channel with no metering story
|
|
537
|
+
// (ICallSandbox.sol:44-51). Aliasing BatchExecutorLib.Call would reintroduce it.
|
|
538
|
+
{
|
|
539
|
+
name: "proposeWithSandbox",
|
|
540
|
+
type: "function",
|
|
541
|
+
stateMutability: "nonpayable",
|
|
542
|
+
inputs: [
|
|
543
|
+
{
|
|
544
|
+
name: "sandbox",
|
|
545
|
+
type: "tuple",
|
|
546
|
+
components: [
|
|
547
|
+
{ name: "funding", type: "uint256" },
|
|
548
|
+
{
|
|
549
|
+
name: "calls",
|
|
550
|
+
type: "tuple[]",
|
|
551
|
+
components: [
|
|
552
|
+
{ name: "target", type: "address" },
|
|
553
|
+
{ name: "data", type: "bytes" }
|
|
554
|
+
]
|
|
555
|
+
},
|
|
556
|
+
{ name: "declaredTokens", type: "address[]" }
|
|
557
|
+
]
|
|
558
|
+
},
|
|
559
|
+
{ name: "vault", type: "address" },
|
|
560
|
+
{ name: "strategy", type: "address" },
|
|
561
|
+
{ name: "metadataURI", type: "string" },
|
|
562
|
+
{ name: "strategyDuration", type: "uint256" },
|
|
563
|
+
{
|
|
564
|
+
name: "envelope",
|
|
565
|
+
type: "tuple",
|
|
566
|
+
components: [
|
|
567
|
+
{ name: "maxCapital", type: "uint256" },
|
|
568
|
+
{ name: "maxDrawdownBps", type: "uint16" }
|
|
569
|
+
]
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
name: "executeCalls",
|
|
573
|
+
type: "tuple[]",
|
|
574
|
+
components: [
|
|
575
|
+
{ name: "target", type: "address" },
|
|
576
|
+
{ name: "data", type: "bytes" },
|
|
577
|
+
{ name: "value", type: "uint256" }
|
|
578
|
+
]
|
|
579
|
+
},
|
|
580
|
+
{ name: "executeCallCaps", type: "uint256[]" },
|
|
581
|
+
{
|
|
582
|
+
name: "settlementCalls",
|
|
583
|
+
type: "tuple[]",
|
|
584
|
+
components: [
|
|
585
|
+
{ name: "target", type: "address" },
|
|
586
|
+
{ name: "data", type: "bytes" },
|
|
587
|
+
{ name: "value", type: "uint256" }
|
|
588
|
+
]
|
|
589
|
+
},
|
|
590
|
+
{ name: "settlementCallCaps", type: "uint256[]" },
|
|
591
|
+
{
|
|
592
|
+
name: "coProposers",
|
|
593
|
+
type: "tuple[]",
|
|
594
|
+
components: [
|
|
595
|
+
{ name: "agent", type: "address" },
|
|
596
|
+
{ name: "splitBps", type: "uint256" }
|
|
597
|
+
]
|
|
598
|
+
}
|
|
599
|
+
],
|
|
600
|
+
outputs: [{ name: "proposalId", type: "uint256" }]
|
|
601
|
+
},
|
|
602
|
+
// Lifecycle writes
|
|
603
|
+
{
|
|
604
|
+
name: "vote",
|
|
605
|
+
type: "function",
|
|
606
|
+
stateMutability: "nonpayable",
|
|
607
|
+
inputs: [
|
|
608
|
+
{ name: "proposalId", type: "uint256" },
|
|
609
|
+
{ name: "support", type: "uint8" }
|
|
610
|
+
],
|
|
611
|
+
outputs: []
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
name: "executeProposal",
|
|
615
|
+
type: "function",
|
|
616
|
+
stateMutability: "nonpayable",
|
|
617
|
+
inputs: [{ name: "proposalId", type: "uint256" }],
|
|
618
|
+
outputs: []
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
name: "settleProposal",
|
|
622
|
+
type: "function",
|
|
623
|
+
stateMutability: "nonpayable",
|
|
624
|
+
inputs: [{ name: "proposalId", type: "uint256" }],
|
|
625
|
+
outputs: []
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
name: "cancelProposal",
|
|
629
|
+
type: "function",
|
|
630
|
+
stateMutability: "nonpayable",
|
|
631
|
+
inputs: [{ name: "proposalId", type: "uint256" }],
|
|
632
|
+
outputs: []
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
name: "vetoProposal",
|
|
636
|
+
type: "function",
|
|
637
|
+
stateMutability: "nonpayable",
|
|
638
|
+
inputs: [{ name: "proposalId", type: "uint256" }],
|
|
639
|
+
outputs: []
|
|
640
|
+
},
|
|
641
|
+
// Emergency settlement (GovernorEmergency, V1.5). `unstick` is the
|
|
642
|
+
// owner-instant pre-committed-calls path; `emergencySettleWithCalls` commits
|
|
643
|
+
// a new calldata hash and opens a guardian-reviewed window. The removed
|
|
644
|
+
// `emergencySettle` is intentionally absent — it is not on the deployed impl.
|
|
645
|
+
{
|
|
646
|
+
name: "unstick",
|
|
647
|
+
type: "function",
|
|
648
|
+
stateMutability: "nonpayable",
|
|
649
|
+
inputs: [{ name: "proposalId", type: "uint256" }],
|
|
650
|
+
outputs: []
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
name: "emergencySettleWithCalls",
|
|
654
|
+
type: "function",
|
|
655
|
+
stateMutability: "nonpayable",
|
|
656
|
+
inputs: [
|
|
657
|
+
{ name: "proposalId", type: "uint256" },
|
|
658
|
+
{
|
|
659
|
+
name: "calls",
|
|
660
|
+
type: "tuple[]",
|
|
661
|
+
components: [
|
|
662
|
+
{ name: "target", type: "address" },
|
|
663
|
+
{ name: "data", type: "bytes" },
|
|
664
|
+
{ name: "value", type: "uint256" }
|
|
665
|
+
]
|
|
666
|
+
}
|
|
667
|
+
],
|
|
668
|
+
outputs: []
|
|
669
|
+
},
|
|
670
|
+
// Parameter setters (GovernorParameters, V1.5 — owner-instant, no timelock).
|
|
671
|
+
{ name: "setVotingPeriod", type: "function", stateMutability: "nonpayable", inputs: [{ name: "newValue", type: "uint256" }], outputs: [] },
|
|
672
|
+
{ name: "setExecutionWindow", type: "function", stateMutability: "nonpayable", inputs: [{ name: "newValue", type: "uint256" }], outputs: [] },
|
|
673
|
+
{ name: "setVetoThresholdBps", type: "function", stateMutability: "nonpayable", inputs: [{ name: "newValue", type: "uint256" }], outputs: [] },
|
|
674
|
+
{ name: "setMaxPerformanceFeeBps", type: "function", stateMutability: "nonpayable", inputs: [{ name: "newValue", type: "uint256" }], outputs: [] },
|
|
675
|
+
{ name: "setMaxStrategyDuration", type: "function", stateMutability: "nonpayable", inputs: [{ name: "newValue", type: "uint256" }], outputs: [] },
|
|
676
|
+
{ name: "setCooldownPeriod", type: "function", stateMutability: "nonpayable", inputs: [{ name: "newValue", type: "uint256" }], outputs: [] },
|
|
677
|
+
// Reads
|
|
678
|
+
{
|
|
679
|
+
name: "getProposal",
|
|
680
|
+
type: "function",
|
|
681
|
+
stateMutability: "view",
|
|
682
|
+
inputs: [{ name: "proposalId", type: "uint256" }],
|
|
683
|
+
outputs: [
|
|
684
|
+
{
|
|
685
|
+
name: "",
|
|
686
|
+
type: "tuple",
|
|
687
|
+
components: [
|
|
688
|
+
{ name: "id", type: "uint256" },
|
|
689
|
+
{ name: "proposer", type: "address" },
|
|
690
|
+
{ name: "vault", type: "address" },
|
|
691
|
+
{ name: "strategy", type: "address" },
|
|
692
|
+
{ name: "metadataURI", type: "string" },
|
|
693
|
+
{ name: "performanceFeeBps", type: "uint256" },
|
|
694
|
+
{ name: "strategyDuration", type: "uint256" },
|
|
695
|
+
{ name: "votesFor", type: "uint256" },
|
|
696
|
+
{ name: "votesAgainst", type: "uint256" },
|
|
697
|
+
{ name: "votesAbstain", type: "uint256" },
|
|
698
|
+
{ name: "snapshotTimestamp", type: "uint256" },
|
|
699
|
+
{ name: "voteEnd", type: "uint256" },
|
|
700
|
+
{ name: "reviewEnd", type: "uint256" },
|
|
701
|
+
{ name: "executeBy", type: "uint256" },
|
|
702
|
+
{ name: "executedAt", type: "uint256" },
|
|
703
|
+
{ name: "state", type: "uint8" },
|
|
704
|
+
{ name: "vetoThresholdBps", type: "uint256" },
|
|
705
|
+
// This tail was doubly stale: it omitted the fee-recipient snapshot
|
|
706
|
+
// entirely AND still carried `selfManagesFees`, which was removed.
|
|
707
|
+
// Every field is positional, so the first divergence corrupts the
|
|
708
|
+
// decode of everything after it.
|
|
709
|
+
{ name: "snapshotProtocolFeeRecipient", type: "address" },
|
|
710
|
+
{ name: "snapshotGuardiansFeeRecipient", type: "address" },
|
|
711
|
+
{ name: "maxCapital", type: "uint256" },
|
|
712
|
+
{ name: "maxDrawdownBps", type: "uint16" },
|
|
713
|
+
{ name: "envelopeTier", type: "uint8" },
|
|
714
|
+
{ name: "requiredCoverage", type: "uint256" },
|
|
715
|
+
{ name: "proposerBondWood", type: "uint256" },
|
|
716
|
+
{ name: "proposerBondEscrow", type: "address" },
|
|
717
|
+
{
|
|
718
|
+
name: "snapshotMgmtSplit",
|
|
719
|
+
type: "tuple",
|
|
720
|
+
components: [
|
|
721
|
+
{ name: "agentBps", type: "uint16" },
|
|
722
|
+
{ name: "protocolBps", type: "uint16" },
|
|
723
|
+
{ name: "guardianBps", type: "uint16" }
|
|
724
|
+
]
|
|
725
|
+
},
|
|
726
|
+
{
|
|
727
|
+
name: "snapshotPerfSplit",
|
|
728
|
+
type: "tuple",
|
|
729
|
+
components: [
|
|
730
|
+
{ name: "agentBps", type: "uint16" },
|
|
731
|
+
{ name: "protocolBps", type: "uint16" },
|
|
732
|
+
{ name: "guardianBps", type: "uint16" },
|
|
733
|
+
{ name: "ownerBps", type: "uint16" }
|
|
734
|
+
]
|
|
735
|
+
},
|
|
736
|
+
{ name: "proposerBondLedger", type: "address" },
|
|
737
|
+
{ name: "effectiveMaxCapital", type: "uint256" }
|
|
738
|
+
]
|
|
739
|
+
}
|
|
740
|
+
]
|
|
741
|
+
},
|
|
742
|
+
// sandboxPayload — ISyndicateGovernor.sol ~730 / SyndicateGovernor.sol:506.
|
|
743
|
+
// Returns SandboxPayload {funding, calls, declaredTokens}. Calls are
|
|
744
|
+
// ICallSandbox.Call = (address target, bytes data) ONLY — no `value`.
|
|
745
|
+
// Non-sandbox (and unknown) pids return the mapping default: zero funding,
|
|
746
|
+
// empty calls, empty declaredTokens. The contract does not revert.
|
|
747
|
+
{
|
|
748
|
+
name: "sandboxPayload",
|
|
749
|
+
type: "function",
|
|
750
|
+
stateMutability: "view",
|
|
751
|
+
inputs: [{ name: "proposalId", type: "uint256" }],
|
|
752
|
+
outputs: [
|
|
753
|
+
{
|
|
754
|
+
name: "payload",
|
|
755
|
+
type: "tuple",
|
|
756
|
+
components: [
|
|
757
|
+
{ name: "funding", type: "uint256" },
|
|
758
|
+
{
|
|
759
|
+
name: "calls",
|
|
760
|
+
type: "tuple[]",
|
|
761
|
+
components: [
|
|
762
|
+
{ name: "target", type: "address" },
|
|
763
|
+
{ name: "data", type: "bytes" }
|
|
764
|
+
]
|
|
765
|
+
},
|
|
766
|
+
{ name: "declaredTokens", type: "address[]" }
|
|
767
|
+
]
|
|
768
|
+
}
|
|
769
|
+
]
|
|
770
|
+
},
|
|
771
|
+
{
|
|
772
|
+
name: "getProposalState",
|
|
773
|
+
type: "function",
|
|
774
|
+
stateMutability: "view",
|
|
775
|
+
inputs: [{ name: "proposalId", type: "uint256" }],
|
|
776
|
+
outputs: [{ name: "", type: "uint8" }]
|
|
777
|
+
},
|
|
778
|
+
{
|
|
779
|
+
name: "getActiveProposal",
|
|
780
|
+
type: "function",
|
|
781
|
+
stateMutability: "view",
|
|
782
|
+
inputs: [],
|
|
783
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
784
|
+
},
|
|
785
|
+
{
|
|
786
|
+
name: "proposalCount",
|
|
787
|
+
type: "function",
|
|
788
|
+
stateMutability: "view",
|
|
789
|
+
inputs: [],
|
|
790
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
791
|
+
},
|
|
792
|
+
{
|
|
793
|
+
name: "getGovernorParams",
|
|
794
|
+
type: "function",
|
|
795
|
+
stateMutability: "view",
|
|
796
|
+
inputs: [],
|
|
797
|
+
outputs: [
|
|
798
|
+
{
|
|
799
|
+
name: "",
|
|
800
|
+
type: "tuple",
|
|
801
|
+
components: [
|
|
802
|
+
{ name: "votingPeriod", type: "uint256" },
|
|
803
|
+
{ name: "executionWindow", type: "uint256" },
|
|
804
|
+
{ name: "vetoThresholdBps", type: "uint256" },
|
|
805
|
+
{ name: "maxPerformanceFeeBps", type: "uint256" },
|
|
806
|
+
{ name: "cooldownPeriod", type: "uint256" },
|
|
807
|
+
{ name: "collaborationWindow", type: "uint256" },
|
|
808
|
+
{ name: "maxCoProposers", type: "uint256" },
|
|
809
|
+
{ name: "minStrategyDuration", type: "uint256" },
|
|
810
|
+
{ name: "maxStrategyDuration", type: "uint256" }
|
|
811
|
+
]
|
|
812
|
+
}
|
|
813
|
+
]
|
|
814
|
+
},
|
|
815
|
+
{
|
|
816
|
+
name: "protocolFeeRecipient",
|
|
817
|
+
type: "function",
|
|
818
|
+
stateMutability: "view",
|
|
819
|
+
inputs: [],
|
|
820
|
+
outputs: [{ name: "", type: "address" }]
|
|
821
|
+
},
|
|
822
|
+
{
|
|
823
|
+
name: "FeeClamped",
|
|
824
|
+
type: "event",
|
|
825
|
+
inputs: [
|
|
826
|
+
{ name: "proposalId", type: "uint256", indexed: true },
|
|
827
|
+
{ name: "snapshotted", type: "uint256", indexed: true },
|
|
828
|
+
{ name: "clamped", type: "uint256", indexed: true }
|
|
829
|
+
]
|
|
830
|
+
}
|
|
831
|
+
];
|
|
832
|
+
var SYNDICATE_FACTORY_ABI = [
|
|
833
|
+
{
|
|
834
|
+
name: "createSyndicate",
|
|
835
|
+
type: "function",
|
|
836
|
+
stateMutability: "nonpayable",
|
|
837
|
+
inputs: [
|
|
838
|
+
{ name: "creatorAgentId", type: "uint256" },
|
|
839
|
+
{
|
|
840
|
+
name: "config",
|
|
841
|
+
type: "tuple",
|
|
842
|
+
components: [
|
|
843
|
+
{ name: "metadataURI", type: "string" },
|
|
844
|
+
{ name: "asset", type: "address" },
|
|
845
|
+
{ name: "name", type: "string" },
|
|
846
|
+
{ name: "symbol", type: "string" },
|
|
847
|
+
{ name: "openDeposits", type: "bool" },
|
|
848
|
+
{ name: "subdomain", type: "string" }
|
|
849
|
+
]
|
|
850
|
+
}
|
|
851
|
+
],
|
|
852
|
+
outputs: [
|
|
853
|
+
{ name: "syndicateId", type: "uint256" },
|
|
854
|
+
{ name: "vault", type: "address" }
|
|
855
|
+
]
|
|
856
|
+
},
|
|
857
|
+
{
|
|
858
|
+
name: "syndicateCount",
|
|
859
|
+
type: "function",
|
|
860
|
+
stateMutability: "view",
|
|
861
|
+
inputs: [],
|
|
862
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
name: "governorOf",
|
|
866
|
+
type: "function",
|
|
867
|
+
stateMutability: "view",
|
|
868
|
+
inputs: [{ name: "vault", type: "address" }],
|
|
869
|
+
outputs: [{ name: "", type: "address" }]
|
|
870
|
+
},
|
|
871
|
+
{
|
|
872
|
+
name: "protocolConfig",
|
|
873
|
+
type: "function",
|
|
874
|
+
stateMutability: "view",
|
|
875
|
+
inputs: [],
|
|
876
|
+
outputs: [{ name: "", type: "address" }]
|
|
877
|
+
},
|
|
878
|
+
{
|
|
879
|
+
name: "subdomainToSyndicate",
|
|
880
|
+
type: "function",
|
|
881
|
+
stateMutability: "view",
|
|
882
|
+
inputs: [{ name: "subdomain", type: "string" }],
|
|
883
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
884
|
+
},
|
|
885
|
+
{
|
|
886
|
+
name: "vaultToSyndicate",
|
|
887
|
+
type: "function",
|
|
888
|
+
stateMutability: "view",
|
|
889
|
+
inputs: [{ name: "vault", type: "address" }],
|
|
890
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
891
|
+
},
|
|
892
|
+
{
|
|
893
|
+
name: "syndicates",
|
|
894
|
+
type: "function",
|
|
895
|
+
stateMutability: "view",
|
|
896
|
+
inputs: [{ name: "id", type: "uint256" }],
|
|
897
|
+
outputs: [
|
|
898
|
+
{ name: "id", type: "uint256" },
|
|
899
|
+
{ name: "vault", type: "address" },
|
|
900
|
+
{ name: "creator", type: "address" },
|
|
901
|
+
{ name: "metadataURI", type: "string" },
|
|
902
|
+
{ name: "createdAt", type: "uint256" },
|
|
903
|
+
{ name: "active", type: "bool" },
|
|
904
|
+
{ name: "subdomain", type: "string" }
|
|
905
|
+
]
|
|
906
|
+
},
|
|
907
|
+
{
|
|
908
|
+
name: "getAllActiveSyndicates",
|
|
909
|
+
type: "function",
|
|
910
|
+
stateMutability: "view",
|
|
911
|
+
inputs: [],
|
|
912
|
+
outputs: [
|
|
913
|
+
{
|
|
914
|
+
name: "",
|
|
915
|
+
type: "tuple[]",
|
|
916
|
+
components: [
|
|
917
|
+
{ name: "id", type: "uint256" },
|
|
918
|
+
{ name: "vault", type: "address" },
|
|
919
|
+
{ name: "creator", type: "address" },
|
|
920
|
+
{ name: "metadataURI", type: "string" },
|
|
921
|
+
{ name: "createdAt", type: "uint256" },
|
|
922
|
+
{ name: "active", type: "bool" },
|
|
923
|
+
{ name: "subdomain", type: "string" }
|
|
924
|
+
]
|
|
925
|
+
}
|
|
926
|
+
]
|
|
927
|
+
}
|
|
928
|
+
];
|
|
929
|
+
var PROTOCOL_CONFIG_ABI = [
|
|
930
|
+
{
|
|
931
|
+
name: "protocolFeeBps",
|
|
932
|
+
type: "function",
|
|
933
|
+
stateMutability: "view",
|
|
934
|
+
inputs: [],
|
|
935
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
936
|
+
},
|
|
937
|
+
{
|
|
938
|
+
name: "guardianFeeBps",
|
|
939
|
+
type: "function",
|
|
940
|
+
stateMutability: "view",
|
|
941
|
+
inputs: [],
|
|
942
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
943
|
+
},
|
|
944
|
+
{
|
|
945
|
+
name: "protocolFeeRecipient",
|
|
946
|
+
type: "function",
|
|
947
|
+
stateMutability: "view",
|
|
948
|
+
inputs: [],
|
|
949
|
+
outputs: [{ name: "", type: "address" }]
|
|
950
|
+
},
|
|
951
|
+
// Protocol-owner setter — protocolFeeBps lives here, not on the governor.
|
|
952
|
+
{
|
|
953
|
+
name: "setProtocolFeeBps",
|
|
954
|
+
type: "function",
|
|
955
|
+
stateMutability: "nonpayable",
|
|
956
|
+
inputs: [{ name: "newValue", type: "uint256" }],
|
|
957
|
+
outputs: []
|
|
958
|
+
}
|
|
959
|
+
];
|
|
960
|
+
var GUARDIAN_REGISTRY_ABI = [
|
|
961
|
+
// Guardian stake
|
|
962
|
+
{
|
|
963
|
+
name: "stakeAsGuardian",
|
|
964
|
+
type: "function",
|
|
965
|
+
stateMutability: "nonpayable",
|
|
966
|
+
inputs: [
|
|
967
|
+
{ name: "amount", type: "uint256" },
|
|
968
|
+
{ name: "agentId", type: "uint256" }
|
|
969
|
+
],
|
|
970
|
+
outputs: []
|
|
971
|
+
},
|
|
972
|
+
{
|
|
973
|
+
name: "requestUnstakeGuardian",
|
|
974
|
+
type: "function",
|
|
975
|
+
stateMutability: "nonpayable",
|
|
976
|
+
inputs: [],
|
|
977
|
+
outputs: []
|
|
978
|
+
},
|
|
979
|
+
{
|
|
980
|
+
name: "cancelUnstakeGuardian",
|
|
981
|
+
type: "function",
|
|
982
|
+
stateMutability: "nonpayable",
|
|
983
|
+
inputs: [],
|
|
984
|
+
outputs: []
|
|
985
|
+
},
|
|
986
|
+
{
|
|
987
|
+
name: "claimUnstakeGuardian",
|
|
988
|
+
type: "function",
|
|
989
|
+
stateMutability: "nonpayable",
|
|
990
|
+
inputs: [],
|
|
991
|
+
outputs: []
|
|
992
|
+
},
|
|
993
|
+
// Delegation and commission fragments removed (SHE-86): delegateStake,
|
|
994
|
+
// requestUnstakeDelegation, cancelUnstakeDelegation, claimUnstakeDelegation
|
|
995
|
+
// and setCommission do not exist on the post-audit contracts.
|
|
996
|
+
// Reads
|
|
997
|
+
{
|
|
998
|
+
name: "guardianStake",
|
|
999
|
+
type: "function",
|
|
1000
|
+
stateMutability: "view",
|
|
1001
|
+
inputs: [{ name: "guardian", type: "address" }],
|
|
1002
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
1003
|
+
},
|
|
1004
|
+
{
|
|
1005
|
+
name: "isActiveGuardian",
|
|
1006
|
+
type: "function",
|
|
1007
|
+
stateMutability: "view",
|
|
1008
|
+
inputs: [{ name: "guardian", type: "address" }],
|
|
1009
|
+
outputs: [{ name: "", type: "bool" }]
|
|
1010
|
+
},
|
|
1011
|
+
// Guardian review vote — 3 args, no slashBps. Selector 0x3ad95b6d.
|
|
1012
|
+
// GuardianVoteType: None=0, Approve=1, Block=2 (uint8).
|
|
1013
|
+
{
|
|
1014
|
+
name: "voteOnProposal",
|
|
1015
|
+
type: "function",
|
|
1016
|
+
stateMutability: "nonpayable",
|
|
1017
|
+
inputs: [
|
|
1018
|
+
{ name: "governor", type: "address" },
|
|
1019
|
+
{ name: "proposalId", type: "uint256" },
|
|
1020
|
+
{ name: "support", type: "uint8" }
|
|
1021
|
+
],
|
|
1022
|
+
outputs: []
|
|
1023
|
+
},
|
|
1024
|
+
// Cached review flags. Returns THREE bools (opened, resolved, blocked).
|
|
1025
|
+
// A fourth output is the SHE-88 arity break: viem refuses the 96-byte returndata.
|
|
1026
|
+
{
|
|
1027
|
+
name: "getReviewState",
|
|
1028
|
+
type: "function",
|
|
1029
|
+
stateMutability: "view",
|
|
1030
|
+
inputs: [
|
|
1031
|
+
{ name: "governor", type: "address" },
|
|
1032
|
+
{ name: "proposalId", type: "uint256" }
|
|
1033
|
+
],
|
|
1034
|
+
outputs: [
|
|
1035
|
+
{ name: "opened", type: "bool" },
|
|
1036
|
+
{ name: "resolved", type: "bool" },
|
|
1037
|
+
{ name: "blocked", type: "bool" }
|
|
1038
|
+
]
|
|
1039
|
+
}
|
|
1040
|
+
];
|
|
1041
|
+
var STAKED_WOOD_ABI = [
|
|
1042
|
+
{
|
|
1043
|
+
name: "stakeAsGuardian",
|
|
1044
|
+
type: "function",
|
|
1045
|
+
stateMutability: "nonpayable",
|
|
1046
|
+
inputs: [
|
|
1047
|
+
{ name: "amount", type: "uint256" },
|
|
1048
|
+
{ name: "agentId", type: "uint256" }
|
|
1049
|
+
],
|
|
1050
|
+
outputs: []
|
|
1051
|
+
}
|
|
1052
|
+
];
|
|
1053
|
+
var ERC20_ABI = [
|
|
1054
|
+
{
|
|
1055
|
+
name: "approve",
|
|
1056
|
+
type: "function",
|
|
1057
|
+
stateMutability: "nonpayable",
|
|
1058
|
+
inputs: [
|
|
1059
|
+
{ name: "spender", type: "address" },
|
|
1060
|
+
{ name: "amount", type: "uint256" }
|
|
1061
|
+
],
|
|
1062
|
+
outputs: [{ name: "", type: "bool" }]
|
|
1063
|
+
},
|
|
1064
|
+
{
|
|
1065
|
+
name: "allowance",
|
|
1066
|
+
type: "function",
|
|
1067
|
+
stateMutability: "view",
|
|
1068
|
+
inputs: [
|
|
1069
|
+
{ name: "owner", type: "address" },
|
|
1070
|
+
{ name: "spender", type: "address" }
|
|
1071
|
+
],
|
|
1072
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
1073
|
+
},
|
|
1074
|
+
{
|
|
1075
|
+
name: "balanceOf",
|
|
1076
|
+
type: "function",
|
|
1077
|
+
stateMutability: "view",
|
|
1078
|
+
inputs: [{ name: "account", type: "address" }],
|
|
1079
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
1080
|
+
},
|
|
1081
|
+
{
|
|
1082
|
+
name: "decimals",
|
|
1083
|
+
type: "function",
|
|
1084
|
+
stateMutability: "view",
|
|
1085
|
+
inputs: [],
|
|
1086
|
+
outputs: [{ name: "", type: "uint8" }]
|
|
1087
|
+
},
|
|
1088
|
+
{
|
|
1089
|
+
name: "symbol",
|
|
1090
|
+
type: "function",
|
|
1091
|
+
stateMutability: "view",
|
|
1092
|
+
inputs: [],
|
|
1093
|
+
outputs: [{ name: "", type: "string" }]
|
|
1094
|
+
}
|
|
1095
|
+
];
|
|
1096
|
+
var IDENTITY_REGISTRY_ABI = [
|
|
1097
|
+
{
|
|
1098
|
+
name: "register",
|
|
1099
|
+
type: "function",
|
|
1100
|
+
stateMutability: "nonpayable",
|
|
1101
|
+
inputs: [
|
|
1102
|
+
{ name: "agentURI", type: "string" },
|
|
1103
|
+
{
|
|
1104
|
+
name: "metadata",
|
|
1105
|
+
type: "tuple[]",
|
|
1106
|
+
components: [
|
|
1107
|
+
{ name: "metadataKey", type: "string" },
|
|
1108
|
+
{ name: "metadataValue", type: "bytes" }
|
|
1109
|
+
]
|
|
1110
|
+
}
|
|
1111
|
+
],
|
|
1112
|
+
outputs: [{ name: "agentId", type: "uint256" }]
|
|
1113
|
+
}
|
|
1114
|
+
];
|
|
1115
|
+
var EAS_ABI = [
|
|
1116
|
+
{
|
|
1117
|
+
name: "attest",
|
|
1118
|
+
type: "function",
|
|
1119
|
+
stateMutability: "payable",
|
|
1120
|
+
inputs: [
|
|
1121
|
+
{
|
|
1122
|
+
name: "request",
|
|
1123
|
+
type: "tuple",
|
|
1124
|
+
components: [
|
|
1125
|
+
{ name: "schema", type: "bytes32" },
|
|
1126
|
+
{
|
|
1127
|
+
name: "data",
|
|
1128
|
+
type: "tuple",
|
|
1129
|
+
components: [
|
|
1130
|
+
{ name: "recipient", type: "address" },
|
|
1131
|
+
{ name: "expirationTime", type: "uint64" },
|
|
1132
|
+
{ name: "revocable", type: "bool" },
|
|
1133
|
+
{ name: "refUID", type: "bytes32" },
|
|
1134
|
+
{ name: "data", type: "bytes" },
|
|
1135
|
+
{ name: "value", type: "uint256" }
|
|
1136
|
+
]
|
|
1137
|
+
}
|
|
1138
|
+
]
|
|
1139
|
+
}
|
|
1140
|
+
],
|
|
1141
|
+
outputs: [{ name: "", type: "bytes32" }]
|
|
1142
|
+
}
|
|
1143
|
+
];
|
|
1144
|
+
var STRATEGY_FACTORY_ABI = [
|
|
1145
|
+
{
|
|
1146
|
+
name: "cloneAndInit",
|
|
1147
|
+
type: "function",
|
|
1148
|
+
stateMutability: "nonpayable",
|
|
1149
|
+
inputs: [
|
|
1150
|
+
{ name: "template", type: "address" },
|
|
1151
|
+
{ name: "vault", type: "address" },
|
|
1152
|
+
{ name: "proposer", type: "address" },
|
|
1153
|
+
{ name: "data", type: "bytes" }
|
|
1154
|
+
],
|
|
1155
|
+
outputs: [{ name: "clone", type: "address" }]
|
|
1156
|
+
},
|
|
1157
|
+
{
|
|
1158
|
+
name: "cloneAndInitDeterministic",
|
|
1159
|
+
type: "function",
|
|
1160
|
+
stateMutability: "nonpayable",
|
|
1161
|
+
inputs: [
|
|
1162
|
+
{ name: "template", type: "address" },
|
|
1163
|
+
{ name: "vault", type: "address" },
|
|
1164
|
+
{ name: "proposer", type: "address" },
|
|
1165
|
+
{ name: "data", type: "bytes" },
|
|
1166
|
+
{ name: "salt", type: "bytes32" }
|
|
1167
|
+
],
|
|
1168
|
+
outputs: [{ name: "clone", type: "address" }]
|
|
1169
|
+
},
|
|
1170
|
+
{
|
|
1171
|
+
name: "syndicateFactory",
|
|
1172
|
+
type: "function",
|
|
1173
|
+
stateMutability: "view",
|
|
1174
|
+
inputs: [],
|
|
1175
|
+
outputs: [{ name: "", type: "address" }]
|
|
1176
|
+
}
|
|
1177
|
+
];
|
|
1178
|
+
|
|
1179
|
+
// ../sdk/dist/addresses.js
|
|
1180
|
+
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
1181
|
+
var ZERO_BYTES32 = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
1182
|
+
var NO_GUARDIAN_ERA = {
|
|
1183
|
+
governorBeacon: ZERO_ADDRESS,
|
|
1184
|
+
protocolConfig: ZERO_ADDRESS,
|
|
1185
|
+
priceRouter: ZERO_ADDRESS,
|
|
1186
|
+
tierRegistry: ZERO_ADDRESS,
|
|
1187
|
+
callSandboxImpl: ZERO_ADDRESS,
|
|
1188
|
+
exposureLedger: ZERO_ADDRESS,
|
|
1189
|
+
proposerBondEscrow: ZERO_ADDRESS,
|
|
1190
|
+
challengeGame: ZERO_ADDRESS,
|
|
1191
|
+
tokenCourt: ZERO_ADDRESS
|
|
1192
|
+
};
|
|
1193
|
+
var BASE_EAS = {
|
|
1194
|
+
chainId: CHAIN_IDS.BASE,
|
|
1195
|
+
address: "0x4200000000000000000000000000000000000021",
|
|
1196
|
+
schemas: {
|
|
1197
|
+
joinRequest: "0x1e7ce17b16233977ba913b156033e98f52029f4bee273a4abefe6c15ce11d5ef",
|
|
1198
|
+
agentApproved: "0x1013f7b38f433b2a93fc5ac162482813081c64edd67cea9b5a90698531ddb607"
|
|
1199
|
+
}
|
|
1200
|
+
};
|
|
1201
|
+
var NO_EAS = {
|
|
1202
|
+
chainId: CHAIN_IDS.BASE,
|
|
1203
|
+
address: ZERO_ADDRESS,
|
|
1204
|
+
schemas: { joinRequest: ZERO_BYTES32, agentApproved: ZERO_BYTES32 }
|
|
1205
|
+
};
|
|
1206
|
+
var ROBINHOOD_EAS = {
|
|
1207
|
+
chainId: CHAIN_IDS.ROBINHOOD,
|
|
1208
|
+
address: "0x7d70441Bb10AcE5d9771dc0b6205D89ddf63205B",
|
|
1209
|
+
schemas: {
|
|
1210
|
+
joinRequest: "0x1e7ce17b16233977ba913b156033e98f52029f4bee273a4abefe6c15ce11d5ef",
|
|
1211
|
+
agentApproved: "0x1013f7b38f433b2a93fc5ac162482813081c64edd67cea9b5a90698531ddb607"
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1214
|
+
var BASE = {
|
|
1215
|
+
chainId: CHAIN_IDS.BASE,
|
|
1216
|
+
name: "Base",
|
|
1217
|
+
slug: "base",
|
|
1218
|
+
sherwood: {
|
|
1219
|
+
factory: "0xAC74EC56858d7F1f7618c8e77F65Fc26aDf33c82",
|
|
1220
|
+
guardianRegistry: "0x49E4163b5e4b23F8f3d469Cf6fa197FB6b06A26E",
|
|
1221
|
+
stakedWood: ZERO_ADDRESS,
|
|
1222
|
+
// not in chains/8453.json
|
|
1223
|
+
vaultImpl: "0xfce4bcE08E9C047E4736f75C2B8557e2754Ce36A",
|
|
1224
|
+
batchExecutorLib: "0xbC79FbD5036C1Cc4A9d10BDf8628BF09a558496E",
|
|
1225
|
+
// Not yet deployed on Base — fill in `STRATEGY_FACTORY` from
|
|
1226
|
+
// contracts/chains/8453.json when it lands (issue #387).
|
|
1227
|
+
strategyFactory: ZERO_ADDRESS,
|
|
1228
|
+
// None of the guardian-era contracts are in chains/8453.json yet.
|
|
1229
|
+
...NO_GUARDIAN_ERA
|
|
1230
|
+
},
|
|
1231
|
+
identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
|
|
1232
|
+
eas: BASE_EAS,
|
|
1233
|
+
tokens: {
|
|
1234
|
+
usdc: {
|
|
1235
|
+
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
1236
|
+
symbol: "USDC",
|
|
1237
|
+
decimals: 6
|
|
1238
|
+
},
|
|
1239
|
+
weth: {
|
|
1240
|
+
address: "0x4200000000000000000000000000000000000006",
|
|
1241
|
+
symbol: "WETH",
|
|
1242
|
+
decimals: 18
|
|
1243
|
+
}
|
|
1244
|
+
},
|
|
1245
|
+
explorer: "https://basescan.org",
|
|
1246
|
+
rpcs: ["https://mainnet.base.org"]
|
|
1247
|
+
};
|
|
1248
|
+
var HYPEREVM = {
|
|
1249
|
+
chainId: CHAIN_IDS.HYPEREVM,
|
|
1250
|
+
name: "HyperEVM",
|
|
1251
|
+
slug: "hyperevm",
|
|
1252
|
+
sherwood: {
|
|
1253
|
+
factory: "0xd05Ae0E8bcf13075C29817c805d6Cc14F214393a",
|
|
1254
|
+
guardianRegistry: "0x8b5710EB4e2fA639F364Dcc3F3B30c8f12F460b9",
|
|
1255
|
+
stakedWood: ZERO_ADDRESS,
|
|
1256
|
+
// no known sWOOD deployment
|
|
1257
|
+
vaultImpl: "0x2cbBe36Cf907A2BB410bacB0e4Fd632C7b012846",
|
|
1258
|
+
batchExecutorLib: "0x2c454bEF1b09c8a306a7058b8B510bF0DfF7179D",
|
|
1259
|
+
strategyFactory: ZERO_ADDRESS,
|
|
1260
|
+
// No canonical chain book for HyperEVM — guardian-era contracts unknown.
|
|
1261
|
+
...NO_GUARDIAN_ERA
|
|
1262
|
+
},
|
|
1263
|
+
// ERC-8004 identity is minted on Base; HyperEVM funds reference the
|
|
1264
|
+
// Base-minted token id. Coordination attestations are pinned to Base EAS.
|
|
1265
|
+
identityRegistry: ZERO_ADDRESS,
|
|
1266
|
+
eas: BASE_EAS,
|
|
1267
|
+
tokens: {
|
|
1268
|
+
usdc: {
|
|
1269
|
+
address: "0xb88339CB7199b77E23DB6E890353E22632Ba630f",
|
|
1270
|
+
symbol: "USDC",
|
|
1271
|
+
decimals: 6
|
|
1272
|
+
}
|
|
1273
|
+
},
|
|
1274
|
+
explorer: "https://hyperliquid.cloud.blockscout.com",
|
|
1275
|
+
rpcs: ["https://rpc.hyperliquid.xyz/evm"]
|
|
1276
|
+
};
|
|
1277
|
+
var ROBINHOOD = {
|
|
1278
|
+
chainId: CHAIN_IDS.ROBINHOOD,
|
|
1279
|
+
name: "Robinhood Chain",
|
|
1280
|
+
slug: "robinhood",
|
|
1281
|
+
sherwood: {
|
|
1282
|
+
factory: ZERO_ADDRESS,
|
|
1283
|
+
guardianRegistry: ZERO_ADDRESS,
|
|
1284
|
+
stakedWood: ZERO_ADDRESS,
|
|
1285
|
+
// 4663.json has WOOD, no STAKED_WOOD
|
|
1286
|
+
vaultImpl: ZERO_ADDRESS,
|
|
1287
|
+
batchExecutorLib: ZERO_ADDRESS,
|
|
1288
|
+
strategyFactory: ZERO_ADDRESS,
|
|
1289
|
+
// Sherwood not deployed on Robinhood mainnet — no guardian-era contracts.
|
|
1290
|
+
...NO_GUARDIAN_ERA
|
|
1291
|
+
},
|
|
1292
|
+
// Canonical ERC-8004 IdentityRegistry (verified on-chain — ERC-1967 proxy,
|
|
1293
|
+
// name() = "AgentIdentity"). 4663 is the identity COORDINATION chain the
|
|
1294
|
+
// same way it is the EAS one: agents mint here regardless of fund chain.
|
|
1295
|
+
identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
|
|
1296
|
+
eas: ROBINHOOD_EAS,
|
|
1297
|
+
tokens: {
|
|
1298
|
+
weth: {
|
|
1299
|
+
address: "0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73",
|
|
1300
|
+
symbol: "WETH",
|
|
1301
|
+
decimals: 18
|
|
1302
|
+
}
|
|
1303
|
+
},
|
|
1304
|
+
explorer: "https://robinhoodchain.blockscout.com",
|
|
1305
|
+
rpcs: ["https://rpc.mainnet.chain.robinhood.com"]
|
|
1306
|
+
};
|
|
1307
|
+
var ROBINHOOD_L2 = {
|
|
1308
|
+
chainId: CHAIN_IDS.ROBINHOOD_L2,
|
|
1309
|
+
name: "Robinhood L2 Testnet",
|
|
1310
|
+
slug: "robinhood-l2",
|
|
1311
|
+
// V2 redeploy (chain 46630): fresh core contracts + a full guardian layer
|
|
1312
|
+
// (registry + sWOOD) and a deployed StrategyFactory for the keyless
|
|
1313
|
+
// clone+init path (#387). Post-#421 per-vault-governor redeploy — kept in
|
|
1314
|
+
// sync with the canonical source of truth contracts/chains/46630.json (and
|
|
1315
|
+
// the app's app/src/lib/contracts.ts ROBINHOOD_TESTNET_ADDRESSES). A stale
|
|
1316
|
+
// factory here made /api/v1/funds?chain=46630 read the old deployment and
|
|
1317
|
+
// return {count: 0} while the UI listed funds (issue #459).
|
|
1318
|
+
sherwood: {
|
|
1319
|
+
factory: "0xa91AA45AFF32f52b6357044B02a16EBA775feC0b",
|
|
1320
|
+
guardianRegistry: "0xA400eFcfFc820C6f812203C58ee00423AeCC0903",
|
|
1321
|
+
stakedWood: "0x21A69A6c9814c0d339C57fDdafed3B283702a739",
|
|
1322
|
+
vaultImpl: "0xC57e12d6e2d8Ed49316F0b69c51893CcA44151F7",
|
|
1323
|
+
batchExecutorLib: "0xF3b8db5aa41c7Ce92478A0Fa9C55a6460533eb86",
|
|
1324
|
+
strategyFactory: "0xb683Bb8EEcBc2419BC3801df6FeA88f96657e670",
|
|
1325
|
+
// Guardian-era contracts from contracts/chains/46630.json (SHE-93).
|
|
1326
|
+
governorBeacon: "0x3D46Ec018cd5893b685b4dfdc3921A4Eb64E11d1",
|
|
1327
|
+
protocolConfig: "0xC104Eb6a522d6718cA28F344B2373B29d57FF2E0",
|
|
1328
|
+
priceRouter: "0xDd302ffcfA08071780eC1A2f12BccFB9ba6b6731",
|
|
1329
|
+
tierRegistry: "0x99b8068Dc0F6093466964D581f72d947e3e380DB",
|
|
1330
|
+
callSandboxImpl: "0xf09f6AF7DeBB964eD731376C9Af389F2Ce3d872A",
|
|
1331
|
+
// Not in chains/46630.json (econ-security stack not deployed there yet).
|
|
1332
|
+
exposureLedger: ZERO_ADDRESS,
|
|
1333
|
+
proposerBondEscrow: ZERO_ADDRESS,
|
|
1334
|
+
challengeGame: ZERO_ADDRESS,
|
|
1335
|
+
tokenCourt: ZERO_ADDRESS
|
|
1336
|
+
},
|
|
1337
|
+
// No ERC-8004 registry on 46630. Attestations for funds here are recorded on
|
|
1338
|
+
// the 4663 coordination chain, so this points at the SAME deployment — note
|
|
1339
|
+
// eas.chainId is 4663, not this chain's id. That is deliberate.
|
|
1340
|
+
identityRegistry: ZERO_ADDRESS,
|
|
1341
|
+
eas: ROBINHOOD_EAS,
|
|
1342
|
+
tokens: {
|
|
1343
|
+
wood: {
|
|
1344
|
+
address: "0xCCb4fB59cf40de1E23083037ee81Da1DD747D8d7",
|
|
1345
|
+
symbol: "WOOD",
|
|
1346
|
+
decimals: 18
|
|
1347
|
+
}
|
|
1348
|
+
},
|
|
1349
|
+
explorer: "https://explorer.testnet.chain.robinhood.com",
|
|
1350
|
+
rpcs: ["https://rpc.testnet.chain.robinhood.com"]
|
|
1351
|
+
};
|
|
1352
|
+
var ROBINHOOD_FORK = {
|
|
1353
|
+
chainId: CHAIN_IDS.ROBINHOOD_FORK,
|
|
1354
|
+
name: "Robinhood Chain (fork)",
|
|
1355
|
+
slug: "robinhood-fork",
|
|
1356
|
+
sherwood: {
|
|
1357
|
+
factory: "0x02bc9B9C1A4322D5A5df86DadA0a2a02e45108B9",
|
|
1358
|
+
guardianRegistry: "0xdfEe38C4D7595c9c04AdBcaFE01E4Fd6FD6117f4",
|
|
1359
|
+
stakedWood: "0xD3037D28693cc7BB3DbF1E17B6e01C4ce628f6DF",
|
|
1360
|
+
vaultImpl: "0x9BF51da59B13746e6cCeb1b35c398EDA9776a4Dc",
|
|
1361
|
+
batchExecutorLib: "0xdBdBce2af4222Abe0cCDAc05f84364a50577B5a2",
|
|
1362
|
+
strategyFactory: "0xb3C009aECAeDd5ccC62Ec12eDAAA55F19C4A1eFb",
|
|
1363
|
+
// Guardian-era contracts from sherwood-protocol/chains/9994663.json (SHE-93).
|
|
1364
|
+
governorBeacon: "0x2e8aB422Db9127F1a70d8C38A15903f22B51DA97",
|
|
1365
|
+
protocolConfig: "0x36C45dC52FbC267d8536E8456ADe5255eC83A5ab",
|
|
1366
|
+
// Not in chains/9994663.json (no PriceRouter / CallSandbox on the fork).
|
|
1367
|
+
priceRouter: ZERO_ADDRESS,
|
|
1368
|
+
tierRegistry: "0x42B089B816F994d792481fAa308199E9CF63be9f",
|
|
1369
|
+
callSandboxImpl: ZERO_ADDRESS,
|
|
1370
|
+
exposureLedger: "0x223e2Fd41aD6487333146fc3998d000E74CB1549",
|
|
1371
|
+
proposerBondEscrow: "0xef2D6994faCad11e6CfAF3DaBf7b0B2091009B05",
|
|
1372
|
+
challengeGame: "0x202c1F133006D6706e872433CCAfD729c7A8AE56",
|
|
1373
|
+
tokenCourt: "0x5DC603881f5d77f41bbd4202F1d518263Dfd497f"
|
|
1374
|
+
},
|
|
1375
|
+
// The fork is ephemeral, so attestation WRITES are disabled there (CLI
|
|
1376
|
+
// enforces it). Reads still resolve against the 4663 coordination chain, so
|
|
1377
|
+
// a fork session can display real attestations.
|
|
1378
|
+
identityRegistry: ZERO_ADDRESS,
|
|
1379
|
+
eas: ROBINHOOD_EAS,
|
|
1380
|
+
tokens: {
|
|
1381
|
+
weth: {
|
|
1382
|
+
address: "0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73",
|
|
1383
|
+
symbol: "WETH",
|
|
1384
|
+
decimals: 18
|
|
1385
|
+
},
|
|
1386
|
+
wood: {
|
|
1387
|
+
address: "0xF8BC08092C06dB6148114DCf82AF881F1085f92b",
|
|
1388
|
+
symbol: "WOOD",
|
|
1389
|
+
decimals: 18
|
|
1390
|
+
}
|
|
1391
|
+
},
|
|
1392
|
+
// A Tenderly vnet scopes its blocks under /explorer/vnet/<id> — the bare
|
|
1393
|
+
// dashboard host resolves to the caller's own dashboard, not this fork.
|
|
1394
|
+
explorer: "https://dashboard.tenderly.co/explorer/vnet/6ad5961e-fbca-452f-939f-ca9a8c020933",
|
|
1395
|
+
// PUBLIC endpoint of vnet a3fb16: reads + tx broadcast, no state-cheat
|
|
1396
|
+
// surface. Admin/cheat RPCs stay private (Tenderly dashboard).
|
|
1397
|
+
rpcs: [
|
|
1398
|
+
"https://virtual.robinhood-chain.eu.rpc.tenderly.co/moonwell/wormhole-bridge/231af1"
|
|
1399
|
+
]
|
|
1400
|
+
};
|
|
1401
|
+
var REGISTRY = {
|
|
1402
|
+
[CHAIN_IDS.BASE]: BASE,
|
|
1403
|
+
[CHAIN_IDS.HYPEREVM]: HYPEREVM,
|
|
1404
|
+
[CHAIN_IDS.ROBINHOOD]: ROBINHOOD,
|
|
1405
|
+
[CHAIN_IDS.ROBINHOOD_L2]: ROBINHOOD_L2,
|
|
1406
|
+
[CHAIN_IDS.ROBINHOOD_FORK]: ROBINHOOD_FORK
|
|
1407
|
+
};
|
|
1408
|
+
function getDeployment(chainId) {
|
|
1409
|
+
const entry = REGISTRY[chainId];
|
|
1410
|
+
if (!entry)
|
|
1411
|
+
throw unsupported(`No Sherwood deployment for chain ${chainId}`);
|
|
1412
|
+
return entry;
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
// ../sdk/dist/encoders/vault.js
|
|
1416
|
+
import { encodeFunctionData, getAddress, maxUint256 } from "viem";
|
|
1417
|
+
var WETH_ABI = [
|
|
1418
|
+
{
|
|
1419
|
+
name: "deposit",
|
|
1420
|
+
type: "function",
|
|
1421
|
+
stateMutability: "payable",
|
|
1422
|
+
inputs: [],
|
|
1423
|
+
outputs: []
|
|
1424
|
+
}
|
|
1425
|
+
];
|
|
1426
|
+
var ZERO_VALUE = "0x0";
|
|
1427
|
+
function toHexValue(value) {
|
|
1428
|
+
return `0x${value.toString(16)}`;
|
|
1429
|
+
}
|
|
1430
|
+
function encodeApproveTx(token, spender, amount, chainId) {
|
|
1431
|
+
return {
|
|
1432
|
+
to: token,
|
|
1433
|
+
data: encodeFunctionData({
|
|
1434
|
+
abi: ERC20_ABI,
|
|
1435
|
+
functionName: "approve",
|
|
1436
|
+
args: [spender, amount]
|
|
1437
|
+
}),
|
|
1438
|
+
value: ZERO_VALUE,
|
|
1439
|
+
chainId
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
function encodeDepositTx(vault, assets, receiver, chainId) {
|
|
1443
|
+
return {
|
|
1444
|
+
to: vault,
|
|
1445
|
+
data: encodeFunctionData({
|
|
1446
|
+
abi: SYNDICATE_VAULT_ABI,
|
|
1447
|
+
functionName: "deposit",
|
|
1448
|
+
args: [assets, receiver]
|
|
1449
|
+
}),
|
|
1450
|
+
value: ZERO_VALUE,
|
|
1451
|
+
chainId
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
function encodeDeposit(args, chainId) {
|
|
1455
|
+
if (args.assets <= 0n) {
|
|
1456
|
+
throw usage("`assets` must be > 0");
|
|
1457
|
+
}
|
|
1458
|
+
const vault = getAddress(args.vault);
|
|
1459
|
+
const receiver = getAddress(args.receiver);
|
|
1460
|
+
const asset = getAddress(args.asset);
|
|
1461
|
+
const txs = [];
|
|
1462
|
+
let wrapped = false;
|
|
1463
|
+
if (args.wrapEth) {
|
|
1464
|
+
const weth = getDeployment(chainId).tokens.weth;
|
|
1465
|
+
if (!weth || getAddress(weth.address) !== asset) {
|
|
1466
|
+
throw usage("`wrapEth` is only valid for WETH vaults \u2014 the vault asset must be the chain's WETH.");
|
|
1467
|
+
}
|
|
1468
|
+
const held = args.currentWethBalance ?? 0n;
|
|
1469
|
+
const shortfall = held >= args.assets ? 0n : args.assets - held;
|
|
1470
|
+
if (shortfall > 0n) {
|
|
1471
|
+
txs.push({
|
|
1472
|
+
to: asset,
|
|
1473
|
+
data: encodeFunctionData({ abi: WETH_ABI, functionName: "deposit", args: [] }),
|
|
1474
|
+
value: toHexValue(shortfall),
|
|
1475
|
+
chainId
|
|
1476
|
+
});
|
|
1477
|
+
wrapped = true;
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
const needsApprove = args.currentAllowance === void 0 || args.currentAllowance < args.assets;
|
|
1481
|
+
if (needsApprove) {
|
|
1482
|
+
txs.push(encodeApproveTx(asset, vault, maxUint256, chainId));
|
|
1483
|
+
}
|
|
1484
|
+
txs.push(encodeDepositTx(vault, args.assets, receiver, chainId));
|
|
1485
|
+
const minDecimal = formatUnits(args.assets, args.assetDecimals);
|
|
1486
|
+
let description;
|
|
1487
|
+
if (wrapped) {
|
|
1488
|
+
description = needsApprove ? `Wrap ETH\u2192WETH, approve ${args.assetSymbol} for ${vault}, then deposit ${minDecimal} ${args.assetSymbol}.` : `Wrap ETH\u2192WETH, then deposit ${minDecimal} ${args.assetSymbol}.`;
|
|
1489
|
+
} else {
|
|
1490
|
+
description = needsApprove ? `Approve ${args.assetSymbol} for ${vault}, then deposit ${minDecimal} ${args.assetSymbol}.` : `Deposit ${minDecimal} ${args.assetSymbol} into ${vault}.`;
|
|
1491
|
+
}
|
|
1492
|
+
return {
|
|
1493
|
+
txs,
|
|
1494
|
+
preconditions: [
|
|
1495
|
+
{
|
|
1496
|
+
type: "balance",
|
|
1497
|
+
asset,
|
|
1498
|
+
assetSymbol: args.assetSymbol,
|
|
1499
|
+
min: args.assets.toString(),
|
|
1500
|
+
minDecimal
|
|
1501
|
+
},
|
|
1502
|
+
{
|
|
1503
|
+
type: "allowance",
|
|
1504
|
+
token: asset,
|
|
1505
|
+
spender: vault,
|
|
1506
|
+
min: args.assets.toString(),
|
|
1507
|
+
minDecimal
|
|
1508
|
+
},
|
|
1509
|
+
{ type: "vault-not-locked", vault },
|
|
1510
|
+
{ type: "depositor-approved", vault, depositor: receiver }
|
|
1511
|
+
],
|
|
1512
|
+
description,
|
|
1513
|
+
note: "Both txs are gated by vault state. If `redemptionsLocked()` is true the deposit reverts unless live NAV is available; check the vault read endpoint before broadcasting."
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
function formatUnits(value, decimals) {
|
|
1517
|
+
if (decimals === 0)
|
|
1518
|
+
return value.toString();
|
|
1519
|
+
const negative = value < 0n;
|
|
1520
|
+
const abs = negative ? -value : value;
|
|
1521
|
+
const str = abs.toString().padStart(decimals + 1, "0");
|
|
1522
|
+
const head = str.slice(0, str.length - decimals);
|
|
1523
|
+
const tail = str.slice(str.length - decimals).replace(/0+$/, "");
|
|
1524
|
+
const out = tail.length > 0 ? `${head}.${tail}` : head;
|
|
1525
|
+
return negative ? `-${out}` : out;
|
|
1526
|
+
}
|
|
1527
|
+
function encodeRedeem(args, chainId) {
|
|
1528
|
+
if (args.shares <= 0n)
|
|
1529
|
+
throw usage("`shares` must be > 0");
|
|
1530
|
+
const vault = getAddress(args.vault);
|
|
1531
|
+
const receiver = getAddress(args.receiver);
|
|
1532
|
+
const owner = getAddress(args.owner);
|
|
1533
|
+
return {
|
|
1534
|
+
txs: [
|
|
1535
|
+
{
|
|
1536
|
+
to: vault,
|
|
1537
|
+
data: encodeFunctionData({
|
|
1538
|
+
abi: SYNDICATE_VAULT_ABI,
|
|
1539
|
+
functionName: "redeem",
|
|
1540
|
+
args: [args.shares, receiver, owner]
|
|
1541
|
+
}),
|
|
1542
|
+
value: ZERO_VALUE,
|
|
1543
|
+
chainId
|
|
1544
|
+
}
|
|
1545
|
+
],
|
|
1546
|
+
preconditions: [
|
|
1547
|
+
{ type: "vault-not-locked", vault }
|
|
1548
|
+
],
|
|
1549
|
+
description: `Redeem ${args.shares.toString()} shares of ${vault} \u2192 ${receiver}.`,
|
|
1550
|
+
note: "Synchronous path. Reverts if the vault has an active proposal AND live NAV is unavailable; in that case use /prepare/request-redeem (queue path) instead."
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
function encodeApproveDepositor(args, chainId) {
|
|
1554
|
+
const vault = getAddress(args.vault);
|
|
1555
|
+
const depositor = getAddress(args.depositor);
|
|
1556
|
+
return {
|
|
1557
|
+
txs: [
|
|
1558
|
+
{
|
|
1559
|
+
to: vault,
|
|
1560
|
+
data: encodeFunctionData({
|
|
1561
|
+
abi: SYNDICATE_VAULT_ABI,
|
|
1562
|
+
functionName: "approveDepositor",
|
|
1563
|
+
args: [depositor]
|
|
1564
|
+
}),
|
|
1565
|
+
value: ZERO_VALUE,
|
|
1566
|
+
chainId
|
|
1567
|
+
}
|
|
1568
|
+
],
|
|
1569
|
+
preconditions: [],
|
|
1570
|
+
description: `Approve depositor ${depositor} on vault ${vault}.`,
|
|
1571
|
+
note: "Vault owner only."
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
function encodeRegisterAgent(args, chainId) {
|
|
1575
|
+
const vault = getAddress(args.vault);
|
|
1576
|
+
const agentAddress = getAddress(args.agentAddress);
|
|
1577
|
+
const id = typeof args.agentId === "bigint" ? args.agentId : BigInt(args.agentId);
|
|
1578
|
+
if (id < 0n)
|
|
1579
|
+
throw usage("`agentId` must be a non-negative integer");
|
|
1580
|
+
return {
|
|
1581
|
+
txs: [
|
|
1582
|
+
{
|
|
1583
|
+
to: vault,
|
|
1584
|
+
data: encodeFunctionData({
|
|
1585
|
+
abi: SYNDICATE_VAULT_ABI,
|
|
1586
|
+
functionName: "registerAgent",
|
|
1587
|
+
args: [agentAddress, id]
|
|
1588
|
+
}),
|
|
1589
|
+
value: ZERO_VALUE,
|
|
1590
|
+
chainId
|
|
1591
|
+
}
|
|
1592
|
+
],
|
|
1593
|
+
preconditions: [],
|
|
1594
|
+
description: `Register agent ${agentAddress} (id ${id.toString()}) on vault ${vault}.`,
|
|
1595
|
+
note: "Vault owner only. The ERC-8004 NFT must be owned by `agentAddress` or by the vault owner at registration time."
|
|
1596
|
+
};
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
// ../sdk/dist/encoders/governor.js
|
|
1600
|
+
import { encodeFunctionData as encodeFunctionData2, getAddress as getAddress2, isAddress as isAddress2 } from "viem";
|
|
1601
|
+
var ZERO_VALUE2 = "0x0";
|
|
1602
|
+
var VOTE_TYPES = {
|
|
1603
|
+
For: 0,
|
|
1604
|
+
Against: 1,
|
|
1605
|
+
Abstain: 2
|
|
1606
|
+
};
|
|
1607
|
+
function governorTx(governor, chainId, data) {
|
|
1608
|
+
return {
|
|
1609
|
+
to: governor,
|
|
1610
|
+
data,
|
|
1611
|
+
value: ZERO_VALUE2,
|
|
1612
|
+
chainId
|
|
1613
|
+
};
|
|
1614
|
+
}
|
|
1615
|
+
function parseGovernor(value) {
|
|
1616
|
+
if (typeof value !== "string" || !isAddress2(value)) {
|
|
1617
|
+
throw usage("`governor` must be the vault's governor address \u2014 resolve it with `resolveGovernor(client, chainId, vault)` (factory.governorOf)");
|
|
1618
|
+
}
|
|
1619
|
+
return getAddress2(value);
|
|
1620
|
+
}
|
|
1621
|
+
function parseProposalId(value) {
|
|
1622
|
+
if (typeof value === "bigint") {
|
|
1623
|
+
if (value < 0n)
|
|
1624
|
+
throw usage("`proposalId` must be a non-negative integer");
|
|
1625
|
+
return value;
|
|
1626
|
+
}
|
|
1627
|
+
if (typeof value === "number") {
|
|
1628
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
1629
|
+
throw usage("`proposalId` must be a non-negative integer");
|
|
1630
|
+
}
|
|
1631
|
+
return BigInt(value);
|
|
1632
|
+
}
|
|
1633
|
+
if (!/^\d+$/.test(value)) {
|
|
1634
|
+
throw usage(`\`proposalId\` must be a non-negative integer string, got "${value}"`);
|
|
1635
|
+
}
|
|
1636
|
+
return BigInt(value);
|
|
1637
|
+
}
|
|
1638
|
+
function encodeVote(args, chainId) {
|
|
1639
|
+
const governor = parseGovernor(args.governor);
|
|
1640
|
+
const support = VOTE_TYPES[args.vote];
|
|
1641
|
+
if (support === void 0) {
|
|
1642
|
+
throw usage(`Unsupported vote "${args.vote}" \u2014 must be one of: ${Object.keys(VOTE_TYPES).join(", ")}`);
|
|
1643
|
+
}
|
|
1644
|
+
const proposalId = parseProposalId(args.proposalId);
|
|
1645
|
+
const data = encodeFunctionData2({
|
|
1646
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1647
|
+
functionName: "vote",
|
|
1648
|
+
args: [proposalId, support]
|
|
1649
|
+
});
|
|
1650
|
+
return {
|
|
1651
|
+
txs: [governorTx(governor, chainId, data)],
|
|
1652
|
+
preconditions: [],
|
|
1653
|
+
description: `Vote ${args.vote} on proposal ${proposalId.toString()}.`,
|
|
1654
|
+
note: "Voter must hold ERC20Votes shares of the vault at the proposal's snapshot timestamp; double-voting reverts. Use the proposal read endpoint to check `voteEnd` first."
|
|
1655
|
+
};
|
|
1656
|
+
}
|
|
1657
|
+
function lifecycleAction(fn, proposalId, governor, chainId, description, note) {
|
|
1658
|
+
const data = encodeFunctionData2({
|
|
1659
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1660
|
+
functionName: fn,
|
|
1661
|
+
args: [proposalId]
|
|
1662
|
+
});
|
|
1663
|
+
return {
|
|
1664
|
+
txs: [governorTx(governor, chainId, data)],
|
|
1665
|
+
preconditions: [],
|
|
1666
|
+
description,
|
|
1667
|
+
note
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
function encodeExecute(args, chainId) {
|
|
1671
|
+
const id = parseProposalId(args.proposalId);
|
|
1672
|
+
return lifecycleAction("executeProposal", id, parseGovernor(args.governor), chainId, `Execute proposal ${id.toString()} (open the strategy).`, "Reverts if the proposal is not in the `Approved` state. Check `state == Approved` via the proposal read endpoint first.");
|
|
1673
|
+
}
|
|
1674
|
+
function encodeSettle(args, chainId) {
|
|
1675
|
+
const id = parseProposalId(args.proposalId);
|
|
1676
|
+
return lifecycleAction("settleProposal", id, parseGovernor(args.governor), chainId, `Settle proposal ${id.toString()} (close the strategy and distribute fees).`, "Proposer can settle anytime; anyone can settle after the strategy duration elapses.");
|
|
1677
|
+
}
|
|
1678
|
+
function encodeCancel(args, chainId) {
|
|
1679
|
+
const id = parseProposalId(args.proposalId);
|
|
1680
|
+
return lifecycleAction("cancelProposal", id, parseGovernor(args.governor), chainId, `Cancel proposal ${id.toString()} (proposer-only).`, "Only the proposer can cancel, and only while the proposal is in `Pending`, `GuardianReview`, or `Approved`.");
|
|
1681
|
+
}
|
|
1682
|
+
function toBigInt(v, label) {
|
|
1683
|
+
if (typeof v === "bigint")
|
|
1684
|
+
return v;
|
|
1685
|
+
if (typeof v === "number" && Number.isInteger(v) && v >= 0)
|
|
1686
|
+
return BigInt(v);
|
|
1687
|
+
if (typeof v === "string" && /^\d+$/.test(v))
|
|
1688
|
+
return BigInt(v);
|
|
1689
|
+
throw usage(`\`${label}\` must be a non-negative integer (string, number, or bigint)`);
|
|
1690
|
+
}
|
|
1691
|
+
function validateCaps(caps, calls, maxCapital, scope) {
|
|
1692
|
+
if (!Array.isArray(caps)) {
|
|
1693
|
+
throw usage(`\`${scope}\` must be an array of per-call caps, one per call`);
|
|
1694
|
+
}
|
|
1695
|
+
if (caps.length !== calls.length) {
|
|
1696
|
+
throw usage(`\`${scope}\` has ${caps.length} entries for ${calls.length} calls \u2014 every call must declare exactly one cap`);
|
|
1697
|
+
}
|
|
1698
|
+
const out = caps.map((c, i) => toBigInt(c, `${scope}[${i}]`));
|
|
1699
|
+
const sum = out.reduce((a, b) => a + b, 0n);
|
|
1700
|
+
if (sum > maxCapital) {
|
|
1701
|
+
throw usage(`\`${scope}\` sums to ${sum} which exceeds \`envelope.maxCapital\` (${maxCapital})`);
|
|
1702
|
+
}
|
|
1703
|
+
return out;
|
|
1704
|
+
}
|
|
1705
|
+
var ZERO_ADDRESS2 = "0x0000000000000000000000000000000000000000";
|
|
1706
|
+
function validateBatchCall(call, scope, idx) {
|
|
1707
|
+
if (!call || typeof call !== "object") {
|
|
1708
|
+
throw usage(`\`${scope}[${idx}]\` must be an object`);
|
|
1709
|
+
}
|
|
1710
|
+
const c = call;
|
|
1711
|
+
if (typeof c.target !== "string" || !isAddress2(c.target)) {
|
|
1712
|
+
throw usage(`\`${scope}[${idx}].target\` must be a 0x-prefixed address`);
|
|
1713
|
+
}
|
|
1714
|
+
if (typeof c.data !== "string" || !/^0x[0-9a-fA-F]*$/.test(c.data)) {
|
|
1715
|
+
throw usage(`\`${scope}[${idx}].data\` must be a 0x-prefixed hex string`);
|
|
1716
|
+
}
|
|
1717
|
+
let value;
|
|
1718
|
+
if (typeof c.value === "string") {
|
|
1719
|
+
if (!/^\d+$/.test(c.value)) {
|
|
1720
|
+
throw usage(`\`${scope}[${idx}].value\` must be a non-negative integer string`);
|
|
1721
|
+
}
|
|
1722
|
+
value = BigInt(c.value);
|
|
1723
|
+
} else if (typeof c.value === "bigint") {
|
|
1724
|
+
if (c.value < 0n) {
|
|
1725
|
+
throw usage(`\`${scope}[${idx}].value\` must be non-negative`);
|
|
1726
|
+
}
|
|
1727
|
+
value = c.value;
|
|
1728
|
+
} else if (c.value === void 0 || c.value === null) {
|
|
1729
|
+
value = 0n;
|
|
1730
|
+
} else {
|
|
1731
|
+
throw usage(`\`${scope}[${idx}].value\` must be a non-negative integer string (or omitted for 0)`);
|
|
1732
|
+
}
|
|
1733
|
+
return { target: getAddress2(c.target), data: c.data, value };
|
|
1734
|
+
}
|
|
1735
|
+
function encodePropose(args, chainId) {
|
|
1736
|
+
const vault = getAddress2(args.vault);
|
|
1737
|
+
const governor = parseGovernor(args.governor);
|
|
1738
|
+
const strategy = args.strategy === ZERO_ADDRESS2 ? ZERO_ADDRESS2 : getAddress2(args.strategy);
|
|
1739
|
+
if (typeof args.metadataURI !== "string" || args.metadataURI.length === 0) {
|
|
1740
|
+
throw usage("`metadataURI` must be a non-empty string (e.g. ipfs://...)");
|
|
1741
|
+
}
|
|
1742
|
+
const strategyDuration = typeof args.strategyDuration === "bigint" ? args.strategyDuration : BigInt(args.strategyDuration);
|
|
1743
|
+
if (strategyDuration <= 0n) {
|
|
1744
|
+
throw usage("`strategyDuration` must be > 0 (seconds)");
|
|
1745
|
+
}
|
|
1746
|
+
if (!Array.isArray(args.executeCalls)) {
|
|
1747
|
+
throw usage("`executeCalls` must be an array of {target, data, value} objects");
|
|
1748
|
+
}
|
|
1749
|
+
if (!Array.isArray(args.settlementCalls)) {
|
|
1750
|
+
throw usage("`settlementCalls` must be an array of {target, data, value} objects");
|
|
1751
|
+
}
|
|
1752
|
+
const executeCalls = args.executeCalls.map((c, i) => validateBatchCall(c, "executeCalls", i));
|
|
1753
|
+
const settlementCalls = args.settlementCalls.map((c, i) => validateBatchCall(c, "settlementCalls", i));
|
|
1754
|
+
const coProposers = (args.coProposers ?? []).map((cp, i) => {
|
|
1755
|
+
if (!cp || typeof cp !== "object") {
|
|
1756
|
+
throw usage(`\`coProposers[${i}]\` must be an object`);
|
|
1757
|
+
}
|
|
1758
|
+
const agent = cp.agent;
|
|
1759
|
+
const splitBps = cp.splitBps;
|
|
1760
|
+
if (typeof agent !== "string" || !isAddress2(agent)) {
|
|
1761
|
+
throw usage(`\`coProposers[${i}].agent\` must be a 0x-prefixed address`);
|
|
1762
|
+
}
|
|
1763
|
+
let bps;
|
|
1764
|
+
if (typeof splitBps === "string" && /^\d+$/.test(splitBps)) {
|
|
1765
|
+
bps = BigInt(splitBps);
|
|
1766
|
+
} else if (typeof splitBps === "bigint") {
|
|
1767
|
+
bps = splitBps;
|
|
1768
|
+
} else if (typeof splitBps === "number" && Number.isInteger(splitBps) && splitBps >= 0) {
|
|
1769
|
+
bps = BigInt(splitBps);
|
|
1770
|
+
} else {
|
|
1771
|
+
throw usage(`\`coProposers[${i}].splitBps\` must be a non-negative integer (string, number, or bigint)`);
|
|
1772
|
+
}
|
|
1773
|
+
if (bps < 0n || bps > 10000n) {
|
|
1774
|
+
throw usage(`\`coProposers[${i}].splitBps\` must be in [0, 10000]`);
|
|
1775
|
+
}
|
|
1776
|
+
return { agent: getAddress2(agent), splitBps: bps };
|
|
1777
|
+
});
|
|
1778
|
+
if (!args.envelope || typeof args.envelope !== "object") {
|
|
1779
|
+
throw usage("`envelope` must be an object of {maxCapital, maxDrawdownBps}");
|
|
1780
|
+
}
|
|
1781
|
+
const maxCapital = toBigInt(args.envelope.maxCapital, "envelope.maxCapital");
|
|
1782
|
+
if (maxCapital === 0n) {
|
|
1783
|
+
throw usage("`envelope.maxCapital` must be > 0 (the governor rejects zero as ZeroMaxCapital)");
|
|
1784
|
+
}
|
|
1785
|
+
const maxDrawdownBps = args.envelope.maxDrawdownBps;
|
|
1786
|
+
if (!Number.isInteger(maxDrawdownBps) || maxDrawdownBps < 0 || maxDrawdownBps > 1e4) {
|
|
1787
|
+
throw usage("`envelope.maxDrawdownBps` must be an integer in [0, 10000]");
|
|
1788
|
+
}
|
|
1789
|
+
const executeCallCaps = validateCaps(args.executeCallCaps, executeCalls, maxCapital, "executeCallCaps");
|
|
1790
|
+
const settlementCallCaps = validateCaps(args.settlementCallCaps, settlementCalls, maxCapital, "settlementCallCaps");
|
|
1791
|
+
const data = encodeFunctionData2({
|
|
1792
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1793
|
+
functionName: "propose",
|
|
1794
|
+
args: [
|
|
1795
|
+
vault,
|
|
1796
|
+
strategy,
|
|
1797
|
+
args.metadataURI,
|
|
1798
|
+
strategyDuration,
|
|
1799
|
+
{ maxCapital, maxDrawdownBps },
|
|
1800
|
+
executeCalls,
|
|
1801
|
+
executeCallCaps,
|
|
1802
|
+
settlementCalls,
|
|
1803
|
+
settlementCallCaps,
|
|
1804
|
+
coProposers
|
|
1805
|
+
]
|
|
1806
|
+
});
|
|
1807
|
+
return {
|
|
1808
|
+
txs: [governorTx(governor, chainId, data)],
|
|
1809
|
+
preconditions: [
|
|
1810
|
+
{ type: "vault-not-locked", vault }
|
|
1811
|
+
],
|
|
1812
|
+
description: `Propose a strategy on ${vault} (duration ${strategyDuration.toString()}s). Agent fee is snapshotted from the vault's agentFeeBps at propose time, then clamped to maxPerformanceFeeBps at settlement.`,
|
|
1813
|
+
note: "Reverts if the vault has an active proposal (cooldown not elapsed). The strategy address is immutable post-propose; pass address(0) to opt out of live NAV. `coProposers[].splitBps` total must satisfy contract-side constraints."
|
|
1814
|
+
};
|
|
1815
|
+
}
|
|
1816
|
+
var MAX_SANDBOX_CALLS = 32;
|
|
1817
|
+
var MAX_SANDBOX_TOKENS = 16;
|
|
1818
|
+
function validateSandboxCall(call, idx) {
|
|
1819
|
+
if (!call || typeof call !== "object") {
|
|
1820
|
+
throw usage(`\`sandbox.calls[${idx}]\` must be an object of {target, data}`);
|
|
1821
|
+
}
|
|
1822
|
+
const c = call;
|
|
1823
|
+
if ("value" in c && c.value !== void 0 && c.value !== null) {
|
|
1824
|
+
throw usage(`\`sandbox.calls[${idx}]\` must be (address,bytes) only \u2014 ICallSandbox.Call has no \`value\` field (native value would open an unmetered transfer channel). Do not alias BatchExecutorLib.Call.`);
|
|
1825
|
+
}
|
|
1826
|
+
if (typeof c.target !== "string" || !isAddress2(c.target)) {
|
|
1827
|
+
throw usage(`\`sandbox.calls[${idx}].target\` must be a 0x-prefixed address`);
|
|
1828
|
+
}
|
|
1829
|
+
if (getAddress2(c.target) === ZERO_ADDRESS2) {
|
|
1830
|
+
throw usage(`\`sandbox.calls[${idx}].target\` must not be the zero address (the governor reverts ZeroSandboxTarget)`);
|
|
1831
|
+
}
|
|
1832
|
+
if (typeof c.data !== "string" || !/^0x[0-9a-fA-F]*$/.test(c.data)) {
|
|
1833
|
+
throw usage(`\`sandbox.calls[${idx}].data\` must be a 0x-prefixed hex string`);
|
|
1834
|
+
}
|
|
1835
|
+
return { target: getAddress2(c.target), data: c.data };
|
|
1836
|
+
}
|
|
1837
|
+
function validateSandboxPayload(sandbox, maxCapital) {
|
|
1838
|
+
if (!sandbox || typeof sandbox !== "object") {
|
|
1839
|
+
throw usage("`sandbox` must be an object of {funding, calls, declaredTokens}");
|
|
1840
|
+
}
|
|
1841
|
+
const s = sandbox;
|
|
1842
|
+
const funding = toBigInt(s.funding, "sandbox.funding");
|
|
1843
|
+
if (funding === 0n) {
|
|
1844
|
+
throw usage("`sandbox.funding` must be > 0 (the governor rejects zero as ZeroSandboxFunding)");
|
|
1845
|
+
}
|
|
1846
|
+
if (funding > maxCapital) {
|
|
1847
|
+
throw usage(`\`sandbox.funding\` (${funding}) exceeds \`envelope.maxCapital\` (${maxCapital}) \u2014 the sandbox is funded out of the declared envelope, not beside it`);
|
|
1848
|
+
}
|
|
1849
|
+
if (!Array.isArray(s.calls)) {
|
|
1850
|
+
throw usage("`sandbox.calls` must be an array of {target, data} objects (no value)");
|
|
1851
|
+
}
|
|
1852
|
+
if (s.calls.length === 0) {
|
|
1853
|
+
throw usage("`sandbox.calls` must be non-empty (the governor rejects empty as EmptySandboxCalls)");
|
|
1854
|
+
}
|
|
1855
|
+
if (s.calls.length > MAX_SANDBOX_CALLS) {
|
|
1856
|
+
throw usage(`\`sandbox.calls\` has ${s.calls.length} entries; max is ${MAX_SANDBOX_CALLS}`);
|
|
1857
|
+
}
|
|
1858
|
+
const calls = s.calls.map((c, i) => validateSandboxCall(c, i));
|
|
1859
|
+
const rawTokens = s.declaredTokens ?? [];
|
|
1860
|
+
if (!Array.isArray(rawTokens)) {
|
|
1861
|
+
throw usage("`sandbox.declaredTokens` must be an array of addresses");
|
|
1862
|
+
}
|
|
1863
|
+
if (rawTokens.length > MAX_SANDBOX_TOKENS) {
|
|
1864
|
+
throw usage(`\`sandbox.declaredTokens\` has ${rawTokens.length} entries; max is ${MAX_SANDBOX_TOKENS}`);
|
|
1865
|
+
}
|
|
1866
|
+
const declaredTokens = [];
|
|
1867
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1868
|
+
for (let i = 0; i < rawTokens.length; i++) {
|
|
1869
|
+
const t = rawTokens[i];
|
|
1870
|
+
if (typeof t !== "string" || !isAddress2(t)) {
|
|
1871
|
+
throw usage(`\`sandbox.declaredTokens[${i}]\` must be a 0x-prefixed address`);
|
|
1872
|
+
}
|
|
1873
|
+
const addr = getAddress2(t);
|
|
1874
|
+
if (seen.has(addr.toLowerCase())) {
|
|
1875
|
+
throw usage(`\`sandbox.declaredTokens[${i}]\` duplicates ${addr} (the governor reverts DuplicateSandboxToken)`);
|
|
1876
|
+
}
|
|
1877
|
+
seen.add(addr.toLowerCase());
|
|
1878
|
+
declaredTokens.push(addr);
|
|
1879
|
+
}
|
|
1880
|
+
return { funding, calls, declaredTokens };
|
|
1881
|
+
}
|
|
1882
|
+
function encodeProposeWithSandbox(args, chainId) {
|
|
1883
|
+
const vault = getAddress2(args.vault);
|
|
1884
|
+
const governor = parseGovernor(args.governor);
|
|
1885
|
+
const strategy = args.strategy === ZERO_ADDRESS2 ? ZERO_ADDRESS2 : getAddress2(args.strategy);
|
|
1886
|
+
if (typeof args.metadataURI !== "string" || args.metadataURI.length === 0) {
|
|
1887
|
+
throw usage("`metadataURI` must be a non-empty string (e.g. ipfs://...)");
|
|
1888
|
+
}
|
|
1889
|
+
const strategyDuration = typeof args.strategyDuration === "bigint" ? args.strategyDuration : BigInt(args.strategyDuration);
|
|
1890
|
+
if (strategyDuration <= 0n) {
|
|
1891
|
+
throw usage("`strategyDuration` must be > 0 (seconds)");
|
|
1892
|
+
}
|
|
1893
|
+
if (!Array.isArray(args.executeCalls)) {
|
|
1894
|
+
throw usage("`executeCalls` must be an array of {target, data, value} objects");
|
|
1895
|
+
}
|
|
1896
|
+
if (!Array.isArray(args.settlementCalls)) {
|
|
1897
|
+
throw usage("`settlementCalls` must be an array of {target, data, value} objects");
|
|
1898
|
+
}
|
|
1899
|
+
const executeCalls = args.executeCalls.map((c, i) => validateBatchCall(c, "executeCalls", i));
|
|
1900
|
+
const settlementCalls = args.settlementCalls.map((c, i) => validateBatchCall(c, "settlementCalls", i));
|
|
1901
|
+
const coProposers = (args.coProposers ?? []).map((cp, i) => {
|
|
1902
|
+
if (!cp || typeof cp !== "object") {
|
|
1903
|
+
throw usage(`\`coProposers[${i}]\` must be an object`);
|
|
1904
|
+
}
|
|
1905
|
+
const agent = cp.agent;
|
|
1906
|
+
const splitBps = cp.splitBps;
|
|
1907
|
+
if (typeof agent !== "string" || !isAddress2(agent)) {
|
|
1908
|
+
throw usage(`\`coProposers[${i}].agent\` must be a 0x-prefixed address`);
|
|
1909
|
+
}
|
|
1910
|
+
let bps;
|
|
1911
|
+
if (typeof splitBps === "string" && /^\d+$/.test(splitBps)) {
|
|
1912
|
+
bps = BigInt(splitBps);
|
|
1913
|
+
} else if (typeof splitBps === "bigint") {
|
|
1914
|
+
bps = splitBps;
|
|
1915
|
+
} else if (typeof splitBps === "number" && Number.isInteger(splitBps) && splitBps >= 0) {
|
|
1916
|
+
bps = BigInt(splitBps);
|
|
1917
|
+
} else {
|
|
1918
|
+
throw usage(`\`coProposers[${i}].splitBps\` must be a non-negative integer (string, number, or bigint)`);
|
|
1919
|
+
}
|
|
1920
|
+
if (bps < 0n || bps > 10000n) {
|
|
1921
|
+
throw usage(`\`coProposers[${i}].splitBps\` must be in [0, 10000]`);
|
|
1922
|
+
}
|
|
1923
|
+
return { agent: getAddress2(agent), splitBps: bps };
|
|
1924
|
+
});
|
|
1925
|
+
if (!args.envelope || typeof args.envelope !== "object") {
|
|
1926
|
+
throw usage("`envelope` must be an object of {maxCapital, maxDrawdownBps}");
|
|
1927
|
+
}
|
|
1928
|
+
const maxCapital = toBigInt(args.envelope.maxCapital, "envelope.maxCapital");
|
|
1929
|
+
if (maxCapital === 0n) {
|
|
1930
|
+
throw usage("`envelope.maxCapital` must be > 0 (the governor rejects zero as ZeroMaxCapital)");
|
|
1931
|
+
}
|
|
1932
|
+
const maxDrawdownBps = args.envelope.maxDrawdownBps;
|
|
1933
|
+
if (!Number.isInteger(maxDrawdownBps) || maxDrawdownBps < 0 || maxDrawdownBps > 1e4) {
|
|
1934
|
+
throw usage("`envelope.maxDrawdownBps` must be an integer in [0, 10000]");
|
|
1935
|
+
}
|
|
1936
|
+
const executeCallCaps = validateCaps(args.executeCallCaps, executeCalls, maxCapital, "executeCallCaps");
|
|
1937
|
+
const settlementCallCaps = validateCaps(args.settlementCallCaps, settlementCalls, maxCapital, "settlementCallCaps");
|
|
1938
|
+
const sandbox = validateSandboxPayload(args.sandbox, maxCapital);
|
|
1939
|
+
const data = encodeFunctionData2({
|
|
1940
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1941
|
+
functionName: "proposeWithSandbox",
|
|
1942
|
+
args: [
|
|
1943
|
+
sandbox,
|
|
1944
|
+
vault,
|
|
1945
|
+
strategy,
|
|
1946
|
+
args.metadataURI,
|
|
1947
|
+
strategyDuration,
|
|
1948
|
+
{ maxCapital, maxDrawdownBps },
|
|
1949
|
+
executeCalls,
|
|
1950
|
+
executeCallCaps,
|
|
1951
|
+
settlementCalls,
|
|
1952
|
+
settlementCallCaps,
|
|
1953
|
+
coProposers
|
|
1954
|
+
]
|
|
1955
|
+
});
|
|
1956
|
+
return {
|
|
1957
|
+
txs: [governorTx(governor, chainId, data)],
|
|
1958
|
+
preconditions: [
|
|
1959
|
+
{ type: "vault-not-locked", vault }
|
|
1960
|
+
],
|
|
1961
|
+
description: `Propose a sandbox strategy on ${vault} (funding ${sandbox.funding.toString()}, ${sandbox.calls.length} sandbox call(s), duration ${strategyDuration.toString()}s).`,
|
|
1962
|
+
note: "Reverts if the vault has an active proposal, has no CallSandbox implementation bound, or the payload fails propose-time sandbox gates (empty calls, zero funding, funding > maxCapital, zero target, duplicate declared tokens). ICallSandbox.Call is (address,bytes) only \u2014 no native value."
|
|
1963
|
+
};
|
|
1964
|
+
}
|
|
1965
|
+
function encodeUnstick(args, chainId) {
|
|
1966
|
+
const governor = parseGovernor(args.governor);
|
|
1967
|
+
const id = parseProposalId(args.proposalId);
|
|
1968
|
+
const data = encodeFunctionData2({
|
|
1969
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1970
|
+
functionName: "unstick",
|
|
1971
|
+
args: [id]
|
|
1972
|
+
});
|
|
1973
|
+
return {
|
|
1974
|
+
txs: [governorTx(governor, chainId, data)],
|
|
1975
|
+
preconditions: [],
|
|
1976
|
+
description: `Unstick proposal ${id.toString()} \u2014 run its governance-approved settlement calls (owner-instant, no funds beyond the approved calls).`,
|
|
1977
|
+
note: "Vault-owner only. Valid only when the proposal is Executed AND its strategy duration has elapsed. Runs the proposal's pre-committed settlement calls and finalizes settlement; no owner stake required. Reverts if those calls revert \u2014 if they can't succeed, open an emergency-settle with custom unwind calls instead."
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1980
|
+
function encodeEmergencySettle(args, chainId) {
|
|
1981
|
+
const governor = parseGovernor(args.governor);
|
|
1982
|
+
const id = parseProposalId(args.proposalId);
|
|
1983
|
+
if (!Array.isArray(args.calls)) {
|
|
1984
|
+
throw usage("`calls` must be an array of {target, data, value} objects");
|
|
1985
|
+
}
|
|
1986
|
+
const calls = args.calls.map((c, i) => validateBatchCall(c, "calls", i));
|
|
1987
|
+
const data = encodeFunctionData2({
|
|
1988
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
1989
|
+
functionName: "emergencySettleWithCalls",
|
|
1990
|
+
args: [id, calls]
|
|
1991
|
+
});
|
|
1992
|
+
return {
|
|
1993
|
+
txs: [governorTx(governor, chainId, data)],
|
|
1994
|
+
preconditions: [],
|
|
1995
|
+
description: `Open an emergency-settle review on proposal ${id.toString()} with ${calls.length} unwind call(s).`,
|
|
1996
|
+
note: "Vault-owner only, requires bonded owner stake. Does NOT execute immediately \u2014 it commits the calls' hash and opens a guardian-reviewed window (default ~24h). After the window, finalize with finalizeEmergencySettle; guardians can block within it (owner stake burned on block-quorum)."
|
|
1997
|
+
};
|
|
1998
|
+
}
|
|
1999
|
+
function parseUintArg(value, label) {
|
|
2000
|
+
if (typeof value === "bigint") {
|
|
2001
|
+
if (value < 0n)
|
|
2002
|
+
throw usage(`\`${label}\` must be a non-negative integer`);
|
|
2003
|
+
return value;
|
|
2004
|
+
}
|
|
2005
|
+
if (typeof value === "number") {
|
|
2006
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
2007
|
+
throw usage(`\`${label}\` must be a non-negative integer`);
|
|
2008
|
+
}
|
|
2009
|
+
return BigInt(value);
|
|
2010
|
+
}
|
|
2011
|
+
if (!/^\d+$/.test(value)) {
|
|
2012
|
+
throw usage(`\`${label}\` must be a non-negative integer string, got "${value}"`);
|
|
2013
|
+
}
|
|
2014
|
+
return BigInt(value);
|
|
2015
|
+
}
|
|
2016
|
+
var GOVERNOR_PARAM_SETTERS = {
|
|
2017
|
+
votingPeriod: "setVotingPeriod",
|
|
2018
|
+
executionWindow: "setExecutionWindow",
|
|
2019
|
+
vetoThresholdBps: "setVetoThresholdBps",
|
|
2020
|
+
maxPerformanceFeeBps: "setMaxPerformanceFeeBps",
|
|
2021
|
+
maxStrategyDuration: "setMaxStrategyDuration",
|
|
2022
|
+
cooldownPeriod: "setCooldownPeriod"
|
|
2023
|
+
};
|
|
2024
|
+
function encodeGovernorParam(args, chainId) {
|
|
2025
|
+
const governor = parseGovernor(args.governor);
|
|
2026
|
+
const fn = GOVERNOR_PARAM_SETTERS[args.param];
|
|
2027
|
+
if (!fn) {
|
|
2028
|
+
throw usage(`Unknown governor param "${args.param}" \u2014 must be one of: ${Object.keys(GOVERNOR_PARAM_SETTERS).join(", ")}`);
|
|
2029
|
+
}
|
|
2030
|
+
const value = parseUintArg(args.value, args.param);
|
|
2031
|
+
const data = encodeFunctionData2({
|
|
2032
|
+
abi: SYNDICATE_GOVERNOR_ABI,
|
|
2033
|
+
functionName: fn,
|
|
2034
|
+
args: [value]
|
|
2035
|
+
});
|
|
2036
|
+
return {
|
|
2037
|
+
txs: [governorTx(governor, chainId, data)],
|
|
2038
|
+
preconditions: [],
|
|
2039
|
+
description: `Set governor parameter ${args.param} = ${value.toString()}.`,
|
|
2040
|
+
note: "Vault-owner only. V1.5 has no on-chain parameter timelock \u2014 the change applies immediately on this call. Bounds are validated on-chain; out-of-range values revert."
|
|
2041
|
+
};
|
|
2042
|
+
}
|
|
2043
|
+
function encodeProtocolFeeBps(args, chainId) {
|
|
2044
|
+
if (!isAddress2(args.protocolConfig)) {
|
|
2045
|
+
throw usage(`\`protocolConfig\` must be a valid address, got "${args.protocolConfig}"`);
|
|
2046
|
+
}
|
|
2047
|
+
const protocolConfig = getAddress2(args.protocolConfig);
|
|
2048
|
+
const value = parseUintArg(args.value, "protocolFeeBps");
|
|
2049
|
+
const data = encodeFunctionData2({
|
|
2050
|
+
abi: PROTOCOL_CONFIG_ABI,
|
|
2051
|
+
functionName: "setProtocolFeeBps",
|
|
2052
|
+
args: [value]
|
|
2053
|
+
});
|
|
2054
|
+
return {
|
|
2055
|
+
txs: [governorTx(protocolConfig, chainId, data)],
|
|
2056
|
+
preconditions: [],
|
|
2057
|
+
description: `Set ProtocolConfig protocolFeeBps = ${value.toString()}.`,
|
|
2058
|
+
note: "Protocol-owner only (the protocol multisig), NOT the vault owner. Targets the protocol-wide ProtocolConfig contract. Bounds are validated on-chain; setting a non-zero protocolFeeBps reverts unless protocolFeeRecipient is already set."
|
|
2059
|
+
};
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
// ../sdk/dist/encoders/factory.js
|
|
2063
|
+
import { encodeFunctionData as encodeFunctionData3, getAddress as getAddress3 } from "viem";
|
|
2064
|
+
var ZERO_VALUE3 = "0x0";
|
|
2065
|
+
var SUBDOMAIN_RE = /^[a-z0-9-]{1,63}$/;
|
|
2066
|
+
function encodeCreateFund(args, chainId) {
|
|
2067
|
+
if (typeof args.metadataURI !== "string" || args.metadataURI.length === 0) {
|
|
2068
|
+
throw usage("`metadataURI` must be a non-empty string (e.g. ipfs://...)");
|
|
2069
|
+
}
|
|
2070
|
+
if (typeof args.name !== "string" || args.name.length === 0) {
|
|
2071
|
+
throw usage("`name` must be a non-empty string");
|
|
2072
|
+
}
|
|
2073
|
+
if (typeof args.symbol !== "string" || args.symbol.length === 0) {
|
|
2074
|
+
throw usage("`symbol` must be a non-empty string");
|
|
2075
|
+
}
|
|
2076
|
+
if (typeof args.openDeposits !== "boolean") {
|
|
2077
|
+
throw usage("`openDeposits` must be a boolean");
|
|
2078
|
+
}
|
|
2079
|
+
if (!SUBDOMAIN_RE.test(args.subdomain)) {
|
|
2080
|
+
throw usage("`subdomain` must match /^[a-z0-9-]{1,63}$/ (lowercase letters, digits, hyphens)");
|
|
2081
|
+
}
|
|
2082
|
+
const asset = getAddress3(args.asset);
|
|
2083
|
+
const creatorAgentId = typeof args.creatorAgentId === "bigint" ? args.creatorAgentId : BigInt(args.creatorAgentId);
|
|
2084
|
+
if (creatorAgentId < 0n)
|
|
2085
|
+
throw usage("`creatorAgentId` must be non-negative");
|
|
2086
|
+
const factory = getDeployment(chainId).sherwood.factory;
|
|
2087
|
+
const data = encodeFunctionData3({
|
|
2088
|
+
abi: SYNDICATE_FACTORY_ABI,
|
|
2089
|
+
functionName: "createSyndicate",
|
|
2090
|
+
args: [
|
|
2091
|
+
creatorAgentId,
|
|
2092
|
+
{
|
|
2093
|
+
metadataURI: args.metadataURI,
|
|
2094
|
+
asset,
|
|
2095
|
+
name: args.name,
|
|
2096
|
+
symbol: args.symbol,
|
|
2097
|
+
openDeposits: args.openDeposits,
|
|
2098
|
+
subdomain: args.subdomain
|
|
2099
|
+
}
|
|
2100
|
+
]
|
|
2101
|
+
});
|
|
2102
|
+
return {
|
|
2103
|
+
txs: [{ to: factory, data, value: ZERO_VALUE3, chainId }],
|
|
2104
|
+
preconditions: [
|
|
2105
|
+
// Creator must own the ERC-8004 NFT for `creatorAgentId` and have
|
|
2106
|
+
// pre-staked the owner bond via guardianRegistry.prepareOwnerStake.
|
|
2107
|
+
// No on-chain check from the SDK side — agent verifies upstream.
|
|
2108
|
+
],
|
|
2109
|
+
description: `Create fund "${args.subdomain}" with ${args.openDeposits ? "open" : "whitelisted"} deposits.`,
|
|
2110
|
+
note: "Caller must own the ERC-8004 NFT for `creatorAgentId` and must have called `guardianRegistry.prepareOwnerStake` first to bond the owner stake. The factory's `createSyndicate` deploys a vault proxy + binds the owner stake atomically."
|
|
2111
|
+
};
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
// ../sdk/dist/encoders/strategy.js
|
|
2115
|
+
import { concatHex, encodeAbiParameters, encodeFunctionData as encodeFunctionData4, encodePacked, getAddress as getAddress4, getCreate2Address, isHex, keccak256 } from "viem";
|
|
2116
|
+
var ZERO_ADDRESS3 = "0x0000000000000000000000000000000000000000";
|
|
2117
|
+
var ZERO_VALUE4 = "0x0";
|
|
2118
|
+
var ERC1167_PREFIX = "0x3d602d80600a3d3981f3363d3d373d3d3d363d73";
|
|
2119
|
+
var ERC1167_SUFFIX = "0x5af43d82803e903d91602b57fd5bf3";
|
|
2120
|
+
function predictStrategyCloneAddress(factory, template, salt) {
|
|
2121
|
+
const initCode = concatHex([
|
|
2122
|
+
ERC1167_PREFIX,
|
|
2123
|
+
getAddress4(template),
|
|
2124
|
+
ERC1167_SUFFIX
|
|
2125
|
+
]);
|
|
2126
|
+
return getCreate2Address({
|
|
2127
|
+
from: getAddress4(factory),
|
|
2128
|
+
salt,
|
|
2129
|
+
bytecode: initCode
|
|
2130
|
+
});
|
|
2131
|
+
}
|
|
2132
|
+
function deriveStrategySalt(vault, proposer, template, initData) {
|
|
2133
|
+
return keccak256(encodePacked(["address", "address", "address", "bytes"], [getAddress4(vault), getAddress4(proposer), getAddress4(template), initData]));
|
|
2134
|
+
}
|
|
2135
|
+
function effectiveStrategySalt(vault, salt) {
|
|
2136
|
+
return keccak256(encodeAbiParameters([{ type: "address" }, { type: "bytes32" }], [getAddress4(vault), salt]));
|
|
2137
|
+
}
|
|
2138
|
+
function requireSalt(value) {
|
|
2139
|
+
if (typeof value !== "string" || !isHex(value) || value.length !== 66) {
|
|
2140
|
+
throw usage("`salt` must be a 0x-prefixed 32-byte hex string (66 chars)");
|
|
2141
|
+
}
|
|
2142
|
+
return value;
|
|
2143
|
+
}
|
|
2144
|
+
function encodeStrategyDeploy(args, chainId) {
|
|
2145
|
+
const template = getAddress4(args.template);
|
|
2146
|
+
const vault = getAddress4(args.vault);
|
|
2147
|
+
const proposer = getAddress4(args.proposer);
|
|
2148
|
+
if (typeof args.initData !== "string" || !isHex(args.initData)) {
|
|
2149
|
+
throw usage("`initData` must be a 0x-prefixed hex string");
|
|
2150
|
+
}
|
|
2151
|
+
if (template === ZERO_ADDRESS3) {
|
|
2152
|
+
throw usage("`template` must be a non-zero address");
|
|
2153
|
+
}
|
|
2154
|
+
if (proposer === ZERO_ADDRESS3) {
|
|
2155
|
+
throw usage("`proposer` must be a non-zero address");
|
|
2156
|
+
}
|
|
2157
|
+
const factory = args.factory ? getAddress4(args.factory) : getDeployment(chainId).sherwood.strategyFactory;
|
|
2158
|
+
if (factory === ZERO_ADDRESS3) {
|
|
2159
|
+
throw unavailable(`StrategyFactory is not yet deployed on chain ${chainId} \u2014 mirror its address into the SDK once it lands (issue #387), or pass an explicit \`factory\`.`);
|
|
2160
|
+
}
|
|
2161
|
+
const salt = args.salt ? requireSalt(args.salt) : deriveStrategySalt(vault, proposer, template, args.initData);
|
|
2162
|
+
const clone = predictStrategyCloneAddress(factory, template, effectiveStrategySalt(vault, salt));
|
|
2163
|
+
const data = encodeFunctionData4({
|
|
2164
|
+
abi: STRATEGY_FACTORY_ABI,
|
|
2165
|
+
functionName: "cloneAndInitDeterministic",
|
|
2166
|
+
args: [template, vault, proposer, args.initData, salt]
|
|
2167
|
+
});
|
|
2168
|
+
const tx = { to: factory, data, value: ZERO_VALUE4, chainId };
|
|
2169
|
+
return {
|
|
2170
|
+
txs: [tx],
|
|
2171
|
+
preconditions: [],
|
|
2172
|
+
clone,
|
|
2173
|
+
salt,
|
|
2174
|
+
factory,
|
|
2175
|
+
description: `Deploy a strategy clone of ${template} for vault ${vault} (deterministic \u2014 clone at ${clone}).`,
|
|
2176
|
+
note: "Broadcast this tx from `proposer` \u2014 the factory requires proposer == msg.sender, and msg.sender must be the vault owner or a registered agent. The vault must be registered on the SyndicateFactory and `template` must be on the factory's allowlist (the factory reverts otherwise). The clone address is fixed by (factory, template, vault, salt) \u2014 reference it as the proposal `strategy` and inside executeCalls/settlementCalls. The factory binds the salt to the vault, and `_authClone` lets only the vault's owner/agents deploy there, so the predicted address can't be front-run."
|
|
2177
|
+
};
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
// ../sdk/dist/encoders/identity.js
|
|
2181
|
+
import { encodeFunctionData as encodeFunctionData5, getAddress as getAddress5 } from "viem";
|
|
2182
|
+
var ZERO_ADDRESS4 = "0x0000000000000000000000000000000000000000";
|
|
2183
|
+
var ERC8004_TYPE = "https://eips.ethereum.org/EIPS/eip-8004#registration-v1";
|
|
2184
|
+
var B64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
2185
|
+
function toBase64Utf8(str) {
|
|
2186
|
+
const bin = encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (_, h) => String.fromCharCode(parseInt(h, 16)));
|
|
2187
|
+
let out = "";
|
|
2188
|
+
for (let i = 0; i < bin.length; i += 3) {
|
|
2189
|
+
const a = bin.charCodeAt(i);
|
|
2190
|
+
const hasB = i + 1 < bin.length;
|
|
2191
|
+
const hasC = i + 2 < bin.length;
|
|
2192
|
+
const b = hasB ? bin.charCodeAt(i + 1) : 0;
|
|
2193
|
+
const c = hasC ? bin.charCodeAt(i + 2) : 0;
|
|
2194
|
+
out += B64_ALPHABET[a >> 2];
|
|
2195
|
+
out += B64_ALPHABET[(a & 3) << 4 | b >> 4];
|
|
2196
|
+
out += hasB ? B64_ALPHABET[(b & 15) << 2 | c >> 6] : "=";
|
|
2197
|
+
out += hasC ? B64_ALPHABET[c & 63] : "=";
|
|
2198
|
+
}
|
|
2199
|
+
return out;
|
|
2200
|
+
}
|
|
2201
|
+
function encodeIdentityMint(args, chainId) {
|
|
2202
|
+
const registry = getDeployment(chainId).identityRegistry;
|
|
2203
|
+
if (registry === ZERO_ADDRESS4) {
|
|
2204
|
+
throw unsupported(`ERC-8004 identity registry is not deployed on chain ${chainId} \u2014 mint on Base (8453).`);
|
|
2205
|
+
}
|
|
2206
|
+
if (!args.name)
|
|
2207
|
+
throw usage("`name` is required");
|
|
2208
|
+
if (!args.description)
|
|
2209
|
+
throw usage("`description` is required");
|
|
2210
|
+
const reg = {
|
|
2211
|
+
type: ERC8004_TYPE,
|
|
2212
|
+
name: args.name,
|
|
2213
|
+
description: args.description
|
|
2214
|
+
};
|
|
2215
|
+
if (args.image)
|
|
2216
|
+
reg.image = args.image;
|
|
2217
|
+
reg.services = [];
|
|
2218
|
+
reg.active = args.active ?? true;
|
|
2219
|
+
reg.x402Support = args.x402Support ?? false;
|
|
2220
|
+
const json = JSON.stringify(reg);
|
|
2221
|
+
const dataUri = `data:application/json;base64,${toBase64Utf8(json)}`;
|
|
2222
|
+
return {
|
|
2223
|
+
txs: [
|
|
2224
|
+
{
|
|
2225
|
+
to: getAddress5(registry),
|
|
2226
|
+
data: encodeFunctionData5({
|
|
2227
|
+
abi: IDENTITY_REGISTRY_ABI,
|
|
2228
|
+
functionName: "register",
|
|
2229
|
+
args: [dataUri, []]
|
|
2230
|
+
}),
|
|
2231
|
+
value: "0x0",
|
|
2232
|
+
chainId
|
|
2233
|
+
}
|
|
2234
|
+
],
|
|
2235
|
+
preconditions: [],
|
|
2236
|
+
description: `Mint an ERC-8004 agent identity "${args.name}" on chain ${chainId}.`,
|
|
2237
|
+
note: "ERC-8004 identity is minted on Base. Read the resulting token id from the mint receipt (chainId:tokenId); reference it when creating or joining funds."
|
|
2238
|
+
};
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
// ../sdk/dist/encoders/eas.js
|
|
2242
|
+
import { encodeAbiParameters as encodeAbiParameters2, encodeFunctionData as encodeFunctionData6, getAddress as getAddress6, parseAbiParameters } from "viem";
|
|
2243
|
+
var ZERO_ADDRESS5 = "0x0000000000000000000000000000000000000000";
|
|
2244
|
+
var ZERO_BYTES322 = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
2245
|
+
var JOIN_REQUEST_PARAMS = parseAbiParameters("uint256, uint256, address, string");
|
|
2246
|
+
var AGENT_APPROVED_PARAMS = parseAbiParameters("uint256, uint256, address");
|
|
2247
|
+
var toBig = (x) => typeof x === "bigint" ? x : BigInt(x);
|
|
2248
|
+
function formatMessageWithRef(message, referrerAgentId) {
|
|
2249
|
+
if (referrerAgentId == null)
|
|
2250
|
+
return message;
|
|
2251
|
+
return `[ref:${referrerAgentId}] ${message}`;
|
|
2252
|
+
}
|
|
2253
|
+
function formatMessageWithChain(message, sourceSlug, easSlug) {
|
|
2254
|
+
if (sourceSlug === easSlug)
|
|
2255
|
+
return message;
|
|
2256
|
+
return `[chain:${sourceSlug}] ${message}`;
|
|
2257
|
+
}
|
|
2258
|
+
function buildAttestTx(easAddress, schema, recipient, data, chainId) {
|
|
2259
|
+
return {
|
|
2260
|
+
to: getAddress6(easAddress),
|
|
2261
|
+
data: encodeFunctionData6({
|
|
2262
|
+
abi: EAS_ABI,
|
|
2263
|
+
functionName: "attest",
|
|
2264
|
+
args: [
|
|
2265
|
+
{
|
|
2266
|
+
schema,
|
|
2267
|
+
data: {
|
|
2268
|
+
recipient,
|
|
2269
|
+
expirationTime: 0n,
|
|
2270
|
+
revocable: true,
|
|
2271
|
+
refUID: ZERO_BYTES322,
|
|
2272
|
+
data,
|
|
2273
|
+
value: 0n
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
]
|
|
2277
|
+
}),
|
|
2278
|
+
value: "0x0",
|
|
2279
|
+
chainId
|
|
2280
|
+
};
|
|
2281
|
+
}
|
|
2282
|
+
function encodeJoinRequest(args, chainId) {
|
|
2283
|
+
const dep = getDeployment(chainId);
|
|
2284
|
+
const eas = dep.eas;
|
|
2285
|
+
if (eas.address === ZERO_ADDRESS5) {
|
|
2286
|
+
throw unsupported(`No EAS coordination path on chain ${chainId} \u2014 join requests require a deployment whose attestations are pinned to a coordination chain.`);
|
|
2287
|
+
}
|
|
2288
|
+
const easSlug = getDeployment(eas.chainId).slug;
|
|
2289
|
+
const message = formatMessageWithChain(formatMessageWithRef(args.message, args.referrerAgentId), dep.slug, easSlug);
|
|
2290
|
+
const data = encodeAbiParameters2(JOIN_REQUEST_PARAMS, [
|
|
2291
|
+
toBig(args.fundId),
|
|
2292
|
+
toBig(args.agentId),
|
|
2293
|
+
getAddress6(args.vault),
|
|
2294
|
+
message
|
|
2295
|
+
]);
|
|
2296
|
+
return {
|
|
2297
|
+
txs: [
|
|
2298
|
+
buildAttestTx(eas.address, eas.schemas.joinRequest, getAddress6(args.creator), data, eas.chainId)
|
|
2299
|
+
],
|
|
2300
|
+
preconditions: [],
|
|
2301
|
+
description: `Request to join fund ${toBig(args.fundId).toString()} (vault ${getAddress6(args.vault)}).`,
|
|
2302
|
+
note: "EAS coordination attestation \u2014 submitted on the coordination chain (see txs[0].chainId), not necessarily the fund's chain. Revocable; the creator discovers it via the join-request schema."
|
|
2303
|
+
};
|
|
2304
|
+
}
|
|
2305
|
+
function encodeApproveAgent(args, chainId) {
|
|
2306
|
+
const dep = getDeployment(chainId);
|
|
2307
|
+
const eas = dep.eas;
|
|
2308
|
+
if (eas.address === ZERO_ADDRESS5) {
|
|
2309
|
+
throw unsupported(`No EAS coordination path on chain ${chainId} \u2014 approvals require a deployment whose attestations are pinned to a coordination chain.`);
|
|
2310
|
+
}
|
|
2311
|
+
const registerTx = encodeRegisterAgent({
|
|
2312
|
+
vault: args.vault,
|
|
2313
|
+
agentAddress: args.agentAddress,
|
|
2314
|
+
agentId: toBig(args.agentId)
|
|
2315
|
+
}, chainId).txs[0];
|
|
2316
|
+
const data = encodeAbiParameters2(AGENT_APPROVED_PARAMS, [
|
|
2317
|
+
toBig(args.fundId),
|
|
2318
|
+
toBig(args.agentId),
|
|
2319
|
+
getAddress6(args.vault)
|
|
2320
|
+
]);
|
|
2321
|
+
const attestTx = buildAttestTx(eas.address, eas.schemas.agentApproved, getAddress6(args.agentAddress), data, eas.chainId);
|
|
2322
|
+
return {
|
|
2323
|
+
txs: [registerTx, attestTx],
|
|
2324
|
+
preconditions: [],
|
|
2325
|
+
description: `Approve agent ${toBig(args.agentId).toString()} for fund ${toBig(args.fundId).toString()}: register on the vault (chain ${chainId}) + AGENT_APPROVED attestation on the coordination chain.`,
|
|
2326
|
+
note: "Vault owner only. Two txs, potentially on two chains: registerAgent runs on the fund's chain; the approval attestation runs on the coordination chain (see txs[1].chainId)."
|
|
2327
|
+
};
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
// ../sdk/dist/encoders/guardian.js
|
|
2331
|
+
import { encodeFunctionData as encodeFunctionData7, getAddress as getAddress7 } from "viem";
|
|
2332
|
+
var ZERO_VALUE5 = "0x0";
|
|
2333
|
+
function swoodTx(chainId, data) {
|
|
2334
|
+
const swood = getDeployment(chainId).sherwood.stakedWood;
|
|
2335
|
+
if (swood === "0x0000000000000000000000000000000000000000") {
|
|
2336
|
+
throw unsupported(`sWOOD (StakedWood) is not deployed on chain ${chainId} yet`);
|
|
2337
|
+
}
|
|
2338
|
+
return { to: swood, data, value: ZERO_VALUE5, chainId };
|
|
2339
|
+
}
|
|
2340
|
+
function registryTx(chainId, data) {
|
|
2341
|
+
const registry = getDeployment(chainId).sherwood.guardianRegistry;
|
|
2342
|
+
if (registry === "0x0000000000000000000000000000000000000000") {
|
|
2343
|
+
throw unsupported(`Guardian registry is not deployed on chain ${chainId} yet`);
|
|
2344
|
+
}
|
|
2345
|
+
return { to: registry, data, value: ZERO_VALUE5, chainId };
|
|
2346
|
+
}
|
|
2347
|
+
function parseUint(value, field) {
|
|
2348
|
+
if (typeof value === "bigint") {
|
|
2349
|
+
if (value < 0n)
|
|
2350
|
+
throw usage(`\`${field}\` must be a non-negative integer`);
|
|
2351
|
+
return value;
|
|
2352
|
+
}
|
|
2353
|
+
if (typeof value === "number") {
|
|
2354
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
2355
|
+
throw usage(`\`${field}\` must be a non-negative integer`);
|
|
2356
|
+
}
|
|
2357
|
+
return BigInt(value);
|
|
2358
|
+
}
|
|
2359
|
+
if (!/^\d+$/.test(value)) {
|
|
2360
|
+
throw usage(`\`${field}\` must be a non-negative integer string, got "${value}"`);
|
|
2361
|
+
}
|
|
2362
|
+
return BigInt(value);
|
|
2363
|
+
}
|
|
2364
|
+
function encodeGuardianStake(args, chainId) {
|
|
2365
|
+
if (args.amount <= 0n)
|
|
2366
|
+
throw usage("`amount` must be > 0");
|
|
2367
|
+
const agentId = parseUint(args.agentId, "agentId");
|
|
2368
|
+
const data = encodeFunctionData7({
|
|
2369
|
+
abi: STAKED_WOOD_ABI,
|
|
2370
|
+
functionName: "stakeAsGuardian",
|
|
2371
|
+
args: [args.amount, agentId]
|
|
2372
|
+
});
|
|
2373
|
+
const deployment = getDeployment(chainId);
|
|
2374
|
+
const wood = deployment.tokens.wood?.address;
|
|
2375
|
+
const swood = deployment.sherwood.stakedWood;
|
|
2376
|
+
if (!wood || wood === "0x0000000000000000000000000000000000000000" || swood === "0x0000000000000000000000000000000000000000") {
|
|
2377
|
+
throw unsupported(`WOOD / sWOOD is not deployed on chain ${chainId} yet`);
|
|
2378
|
+
}
|
|
2379
|
+
return {
|
|
2380
|
+
txs: [swoodTx(chainId, data)],
|
|
2381
|
+
preconditions: [
|
|
2382
|
+
// Caller must have approved WOOD for sWOOD (StakedWood); agent should
|
|
2383
|
+
// check this client-side and prepend an ERC20.approve if needed.
|
|
2384
|
+
{
|
|
2385
|
+
type: "allowance",
|
|
2386
|
+
token: wood,
|
|
2387
|
+
spender: swood,
|
|
2388
|
+
min: args.amount.toString(),
|
|
2389
|
+
minDecimal: args.amount.toString()
|
|
2390
|
+
}
|
|
2391
|
+
],
|
|
2392
|
+
description: `Stake ${args.amount.toString()} WOOD as guardian (agent ${agentId.toString()}).`,
|
|
2393
|
+
note: "Caller must have approved WOOD for sWOOD (StakedWood) first. Stake locks the WOOD; use /prepare/guardian-unstake later to start the 7d unstake cooldown."
|
|
2394
|
+
};
|
|
2395
|
+
}
|
|
2396
|
+
var GUARDIAN_UNSTAKE_FN = {
|
|
2397
|
+
request: "requestUnstakeGuardian",
|
|
2398
|
+
cancel: "cancelUnstakeGuardian",
|
|
2399
|
+
claim: "claimUnstakeGuardian"
|
|
2400
|
+
};
|
|
2401
|
+
function encodeGuardianUnstake(args, chainId) {
|
|
2402
|
+
const fn = GUARDIAN_UNSTAKE_FN[args.action];
|
|
2403
|
+
if (!fn) {
|
|
2404
|
+
throw usage(`\`action\` must be one of: ${Object.keys(GUARDIAN_UNSTAKE_FN).join(", ")}`);
|
|
2405
|
+
}
|
|
2406
|
+
const data = encodeFunctionData7({
|
|
2407
|
+
abi: GUARDIAN_REGISTRY_ABI,
|
|
2408
|
+
functionName: fn,
|
|
2409
|
+
args: []
|
|
2410
|
+
});
|
|
2411
|
+
return {
|
|
2412
|
+
txs: [registryTx(chainId, data)],
|
|
2413
|
+
preconditions: [],
|
|
2414
|
+
description: `Guardian unstake ${args.action}.`,
|
|
2415
|
+
note: args.action === "request" ? "Starts a 7-day cooldown. After it elapses, call again with action=claim to withdraw." : args.action === "claim" ? "Withdraws the unstake amount once the 7-day cooldown has elapsed." : "Cancels a pending unstake request and re-activates the guardian."
|
|
2416
|
+
};
|
|
2417
|
+
}
|
|
2418
|
+
function encodeVoteOnProposal(args, chainId) {
|
|
2419
|
+
let governor;
|
|
2420
|
+
try {
|
|
2421
|
+
governor = getAddress7(args.governor);
|
|
2422
|
+
} catch {
|
|
2423
|
+
throw usage("`governor` must be a 0x-prefixed 40-char hex address");
|
|
2424
|
+
}
|
|
2425
|
+
const support = GUARDIAN_VOTE_TYPES.indexOf(args.vote);
|
|
2426
|
+
if (support <= 0) {
|
|
2427
|
+
throw usage("`vote` must be one of: Approve, Block");
|
|
2428
|
+
}
|
|
2429
|
+
const proposalId = parseUint(args.proposalId, "proposalId");
|
|
2430
|
+
const data = encodeFunctionData7({
|
|
2431
|
+
abi: GUARDIAN_REGISTRY_ABI,
|
|
2432
|
+
functionName: "voteOnProposal",
|
|
2433
|
+
args: [governor, proposalId, support]
|
|
2434
|
+
});
|
|
2435
|
+
const underwriting = args.vote === "Approve" ? " APPROVE books coverage against the CALLER'S OWN slashable guardian bond (ExposureLedger.recordApproval). If this proposal is later challenged or slashed, your WOOD stake is at risk \u2014 not another guardian's. An over-exposed guardian books nothing rather than reverting; the vote still lands." : " Block votes do not book coverage. Slash severity is computed at resolveReview from block-side decisiveness.";
|
|
2436
|
+
return {
|
|
2437
|
+
txs: [registryTx(chainId, data)],
|
|
2438
|
+
preconditions: [],
|
|
2439
|
+
description: `Guardian review vote ${args.vote} on proposal ${proposalId.toString()} (governor ${governor}).`,
|
|
2440
|
+
note: `voteOnProposal(governor, proposalId, vote) \u2014 3 args, selector 0x3ad95b6d, no slashBps.${underwriting}`
|
|
2441
|
+
};
|
|
2442
|
+
}
|
|
2443
|
+
|
|
2444
|
+
// ../sdk/dist/reads/funds.js
|
|
2445
|
+
import { getAddress as getAddress8 } from "viem";
|
|
2446
|
+
|
|
2447
|
+
// src/commands/identity.ts
|
|
2448
|
+
var IDENTITY_REGISTRY_ABI2 = [
|
|
2449
|
+
{
|
|
2450
|
+
name: "balanceOf",
|
|
2451
|
+
type: "function",
|
|
2452
|
+
stateMutability: "view",
|
|
2453
|
+
inputs: [{ name: "owner", type: "address" }],
|
|
2454
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
2455
|
+
},
|
|
2456
|
+
{
|
|
2457
|
+
name: "ownerOf",
|
|
2458
|
+
type: "function",
|
|
2459
|
+
stateMutability: "view",
|
|
2460
|
+
inputs: [{ name: "tokenId", type: "uint256" }],
|
|
2461
|
+
outputs: [{ name: "", type: "address" }]
|
|
2462
|
+
}
|
|
2463
|
+
];
|
|
2464
|
+
function getAgent0SDK() {
|
|
2465
|
+
const key = requirePrivateKey();
|
|
2466
|
+
const chain = getChain();
|
|
2467
|
+
const sdkConfig = {
|
|
2468
|
+
chainId: chain.id,
|
|
2469
|
+
rpcUrl: getRpcUrl(),
|
|
2470
|
+
privateKey: key
|
|
2471
|
+
};
|
|
2472
|
+
if (chain.id !== getChainConfig().chain.id) {
|
|
2473
|
+
sdkConfig.registryOverrides = {
|
|
2474
|
+
[chain.id]: {
|
|
2475
|
+
IDENTITY: AGENT_REGISTRY().IDENTITY_REGISTRY,
|
|
2476
|
+
REPUTATION: AGENT_REGISTRY().REPUTATION_REGISTRY
|
|
2477
|
+
}
|
|
2478
|
+
};
|
|
2479
|
+
}
|
|
2480
|
+
return new SDK(sdkConfig);
|
|
2481
|
+
}
|
|
2482
|
+
async function lookupAgentIdByWallet(wallet) {
|
|
2483
|
+
return withNetwork(COORDINATION_NETWORK, async () => {
|
|
2484
|
+
const client = getPublicClient();
|
|
2485
|
+
const registry = AGENT_REGISTRY().IDENTITY_REGISTRY;
|
|
2486
|
+
const balance = await client.readContract({
|
|
2487
|
+
address: registry,
|
|
2488
|
+
abi: [{
|
|
2489
|
+
name: "balanceOf",
|
|
2490
|
+
type: "function",
|
|
2491
|
+
stateMutability: "view",
|
|
2492
|
+
inputs: [{ name: "owner", type: "address" }],
|
|
2493
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
2494
|
+
}],
|
|
2495
|
+
functionName: "balanceOf",
|
|
2496
|
+
args: [wallet]
|
|
2497
|
+
});
|
|
2498
|
+
if (balance === 0n) return null;
|
|
2499
|
+
const logs = await client.getLogs({
|
|
2500
|
+
address: registry,
|
|
2501
|
+
event: {
|
|
2502
|
+
type: "event",
|
|
2503
|
+
name: "Transfer",
|
|
2504
|
+
inputs: [
|
|
2505
|
+
{ indexed: true, name: "from", type: "address" },
|
|
2506
|
+
{ indexed: true, name: "to", type: "address" },
|
|
2507
|
+
{ indexed: true, name: "tokenId", type: "uint256" }
|
|
2508
|
+
]
|
|
2509
|
+
},
|
|
2510
|
+
args: { from: "0x0000000000000000000000000000000000000000", to: wallet },
|
|
2511
|
+
fromBlock: 0n
|
|
2512
|
+
});
|
|
2513
|
+
const last = logs[logs.length - 1];
|
|
2514
|
+
return last?.args?.tokenId ?? null;
|
|
2515
|
+
});
|
|
2516
|
+
}
|
|
2517
|
+
function registerIdentityCommands(program) {
|
|
2518
|
+
const identity = program.command("identity").description("Manage ERC-8004 agent identity (via Agent0 SDK)");
|
|
2519
|
+
identity.command("mint").description("Register a new ERC-8004 agent identity (required before creating/joining funds)").requiredOption("--name <name>", "Agent name (e.g. 'Alpha Seeker Agent')").option("--description <desc>", "Agent description", "Sherwood fund agent").option("--image <uri>", "Agent image URI (IPFS recommended)").action(async (opts) => {
|
|
2520
|
+
if (isCalldataOnly()) {
|
|
2521
|
+
emitCalldata(
|
|
2522
|
+
encodeIdentityMint(
|
|
2523
|
+
{
|
|
2524
|
+
name: opts.name,
|
|
2525
|
+
description: opts.description,
|
|
2526
|
+
image: opts.image
|
|
2527
|
+
},
|
|
2528
|
+
4663
|
|
2529
|
+
)
|
|
2530
|
+
);
|
|
2531
|
+
return;
|
|
2532
|
+
}
|
|
2533
|
+
const account = getAccount();
|
|
2534
|
+
const existingId = getAgentId();
|
|
2535
|
+
if (existingId) {
|
|
2536
|
+
console.log(chalk.yellow(`You already have an agent identity saved: #${existingId}`));
|
|
2537
|
+
console.log(chalk.dim(" Minting a new one anyway. The old ID is not affected."));
|
|
2538
|
+
console.log();
|
|
2539
|
+
}
|
|
2540
|
+
const spinner = ora("Minting ERC-8004 identity on Robinhood mainnet...").start();
|
|
2541
|
+
try {
|
|
2542
|
+
const tokenId = await withNetwork(COORDINATION_NETWORK, async () => {
|
|
2543
|
+
const client = getPublicClient();
|
|
2544
|
+
const balance = await client.getBalance({ address: account.address });
|
|
2545
|
+
if (balance === 0n) {
|
|
2546
|
+
throw new Error(
|
|
2547
|
+
`No ETH on Robinhood mainnet (chain 4663) for ${account.address}. Identity is minted on the coordination chain regardless of your fund's chain; a mint costs well under 0.0001 ETH.`
|
|
2548
|
+
);
|
|
2549
|
+
}
|
|
2550
|
+
const action = encodeIdentityMint(
|
|
2551
|
+
{ name: opts.name, description: opts.description, image: opts.image },
|
|
2552
|
+
4663
|
|
2553
|
+
);
|
|
2554
|
+
const tx = action.txs[0];
|
|
2555
|
+
const hash = await sendTxWithRetry({
|
|
2556
|
+
account,
|
|
2557
|
+
chain: getChain(),
|
|
2558
|
+
to: tx.to,
|
|
2559
|
+
data: tx.data
|
|
2560
|
+
});
|
|
2561
|
+
spinner.text = "Waiting for confirmation...";
|
|
2562
|
+
const receipt = await waitForReceipt(hash);
|
|
2563
|
+
const TRANSFER = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
|
|
2564
|
+
const mint = receipt.logs.find(
|
|
2565
|
+
(l) => l.address.toLowerCase() === tx.to.toLowerCase() && l.topics[0] === TRANSFER
|
|
2566
|
+
);
|
|
2567
|
+
if (!mint?.topics[3]) throw new Error("no Transfer event in mint receipt");
|
|
2568
|
+
return Number(BigInt(mint.topics[3]));
|
|
2569
|
+
});
|
|
2570
|
+
setAgentId(tokenId);
|
|
2571
|
+
spinner.succeed(`Agent identity registered: #${tokenId}`);
|
|
2572
|
+
console.log(chalk.dim(` Chain: Robinhood mainnet (4663) \u2014 the coordination chain`));
|
|
2573
|
+
console.log(chalk.dim(` Name: ${opts.name}`));
|
|
2574
|
+
console.log(chalk.dim(` Owner: ${account.address}`));
|
|
2575
|
+
console.log(chalk.dim(` Saved to ~/.sherwood/config.json`));
|
|
2576
|
+
console.log();
|
|
2577
|
+
console.log(chalk.green("You can now create funds:"));
|
|
2578
|
+
console.log(chalk.dim(` sherwood fund create --agent-id ${tokenId} --subdomain <name> --name <name>`));
|
|
2579
|
+
} catch (err) {
|
|
2580
|
+
spinner.fail("Failed to register identity");
|
|
2581
|
+
console.error(chalk.red(formatContractError(err)));
|
|
2582
|
+
process.exit(1);
|
|
2583
|
+
}
|
|
2584
|
+
});
|
|
2585
|
+
identity.command("load").description("Load an existing ERC-8004 agent identity into your config").requiredOption("--id <tokenId>", "Agent token ID to load").action(async (opts) => {
|
|
2586
|
+
const account = getAccount();
|
|
2587
|
+
const tokenId = Number(opts.id);
|
|
2588
|
+
const spinner = ora(`Verifying ownership of agent #${tokenId}...`).start();
|
|
2589
|
+
try {
|
|
2590
|
+
const owner = await withNetwork(
|
|
2591
|
+
COORDINATION_NETWORK,
|
|
2592
|
+
() => getPublicClient().readContract({
|
|
2593
|
+
address: AGENT_REGISTRY().IDENTITY_REGISTRY,
|
|
2594
|
+
abi: IDENTITY_REGISTRY_ABI2,
|
|
2595
|
+
functionName: "ownerOf",
|
|
2596
|
+
args: [BigInt(tokenId)]
|
|
2597
|
+
})
|
|
2598
|
+
);
|
|
2599
|
+
if (owner.toLowerCase() !== account.address.toLowerCase()) {
|
|
2600
|
+
spinner.fail(`Agent #${tokenId} is owned by ${owner}, not your wallet`);
|
|
2601
|
+
process.exit(1);
|
|
2602
|
+
}
|
|
2603
|
+
setAgentId(tokenId);
|
|
2604
|
+
spinner.succeed(`Agent #${tokenId} loaded and saved to config`);
|
|
2605
|
+
console.log(chalk.dim(` Owner: ${account.address}`));
|
|
2606
|
+
console.log(chalk.dim(` Saved to ~/.sherwood/config.json`));
|
|
2607
|
+
} catch (err) {
|
|
2608
|
+
spinner.fail("Failed to load identity");
|
|
2609
|
+
console.error(chalk.red(formatContractError(err)));
|
|
2610
|
+
process.exit(1);
|
|
2611
|
+
}
|
|
2612
|
+
});
|
|
2613
|
+
identity.command("link-virtuals").description("Link a Virtuals economyOS agent identity (ERC-8004 on Base) to this Sherwood install").option("--wallet <address>", "economyOS agent wallet (default: auto-detect via `acp wallet address`)").option("--agent-id <tokenId>", "ERC-8004 token ID on the issuing chain (default: resolve via Agent0 SDK)").option("--chain-id <id>", "Issuing chain ID (4663 = Robinhood Chain, 8453 = Base, 84532 = Base Sepolia)", String(DEFAULT_ISSUING_CHAIN_ID)).option("--binding-sig <hex>", "EIP-191 signature of the binding message by the economyOS wallet").option("--wallet-only", "Link without an ERC-8004 registration \u2014 the economyOS wallet + binding signature are the identity (agentId 0)").action(async (opts) => {
|
|
2614
|
+
const account = getAccount();
|
|
2615
|
+
const issuingChainId = opts.walletOnly ? WALLET_ONLY_CHAIN_ID : parseInt(opts.chainId, 10);
|
|
2616
|
+
if (!opts.walletOnly && !ISSUING_CHAINS[issuingChainId]) {
|
|
2617
|
+
console.error(chalk.red(`Unsupported issuing chain ${opts.chainId}. Supported: ${Object.keys(ISSUING_CHAINS).join(", ")}`));
|
|
2618
|
+
process.exit(1);
|
|
2619
|
+
}
|
|
2620
|
+
const spinner = ora("Resolving economyOS agent...").start();
|
|
2621
|
+
try {
|
|
2622
|
+
let virtualsWallet = opts.wallet;
|
|
2623
|
+
let virtualsAgentId;
|
|
2624
|
+
if (await detectAcp()) {
|
|
2625
|
+
const who = await acpWhoami();
|
|
2626
|
+
virtualsAgentId = who?.agentId;
|
|
2627
|
+
if (!virtualsWallet) {
|
|
2628
|
+
virtualsWallet = await acpWalletAddress() ?? void 0;
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
if (!virtualsWallet) {
|
|
2632
|
+
spinner.fail("Could not resolve the economyOS agent wallet");
|
|
2633
|
+
console.error(chalk.dim(" Install + authenticate the acp CLI (npm i -g @virtuals-protocol/acp-cli; acp configure)"));
|
|
2634
|
+
console.error(chalk.dim(" or pass --wallet <address> explicitly."));
|
|
2635
|
+
process.exit(1);
|
|
2636
|
+
}
|
|
2637
|
+
let tokenId;
|
|
2638
|
+
if (opts.walletOnly) {
|
|
2639
|
+
tokenId = 0n;
|
|
2640
|
+
} else if (opts.agentId) {
|
|
2641
|
+
tokenId = BigInt(opts.agentId);
|
|
2642
|
+
} else {
|
|
2643
|
+
spinner.text = `Searching ERC-8004 registry on chain ${issuingChainId} for ${virtualsWallet}...`;
|
|
2644
|
+
const sdk = new SDK({
|
|
2645
|
+
chainId: issuingChainId,
|
|
2646
|
+
rpcUrl: ISSUING_CHAINS[issuingChainId].chain.rpcUrls.default.http[0],
|
|
2647
|
+
privateKey: generatePrivateKey()
|
|
2648
|
+
});
|
|
2649
|
+
const results = await sdk.searchAgents({
|
|
2650
|
+
walletAddress: virtualsWallet,
|
|
2651
|
+
chains: [issuingChainId]
|
|
2652
|
+
});
|
|
2653
|
+
if (results.length === 0) {
|
|
2654
|
+
spinner.fail(`No ERC-8004 registration found for ${virtualsWallet} on chain ${issuingChainId}`);
|
|
2655
|
+
console.error(chalk.dim(" Register first via the Virtuals CLI:"));
|
|
2656
|
+
console.error(chalk.dim(` acp agent register-erc8004 --agent-id ${virtualsAgentId ?? "<id>"} --chain-id ${issuingChainId}`));
|
|
2657
|
+
console.error(chalk.dim(" Then re-run this command (or pass --agent-id <tokenId> directly)."));
|
|
2658
|
+
console.error(chalk.dim(" No registration possible yet? Link without one: --wallet-only"));
|
|
2659
|
+
process.exit(1);
|
|
2660
|
+
}
|
|
2661
|
+
if (results.length > 1) {
|
|
2662
|
+
spinner.fail(`Wallet ${virtualsWallet} has ${results.length} ERC-8004 registrations on chain ${issuingChainId} \u2014 pick one explicitly`);
|
|
2663
|
+
for (const a of results) {
|
|
2664
|
+
const idStr2 = String(a.agentId);
|
|
2665
|
+
const tid = idStr2.includes(":") ? idStr2.split(":")[1] : idStr2;
|
|
2666
|
+
console.error(chalk.dim(` #${tid} ${a.name || "(no name)"}`));
|
|
2667
|
+
}
|
|
2668
|
+
console.error(chalk.green(" Re-run with: sherwood identity link-virtuals --agent-id <tokenId>"));
|
|
2669
|
+
process.exit(1);
|
|
2670
|
+
}
|
|
2671
|
+
const idStr = String(results[0].agentId);
|
|
2672
|
+
tokenId = BigInt(idStr.includes(":") ? idStr.split(":")[1] : idStr);
|
|
2673
|
+
}
|
|
2674
|
+
if (tokenId > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
2675
|
+
spinner.fail(`Token ID ${tokenId} exceeds Number.MAX_SAFE_INTEGER \u2014 cannot be stored in config`);
|
|
2676
|
+
process.exit(1);
|
|
2677
|
+
}
|
|
2678
|
+
if (!opts.walletOnly) {
|
|
2679
|
+
spinner.text = `Verifying ownerOf(#${tokenId}) on chain ${issuingChainId}...`;
|
|
2680
|
+
const owner = await ownerOfOnIssuingChain(issuingChainId, tokenId);
|
|
2681
|
+
if (owner.toLowerCase() !== virtualsWallet.toLowerCase()) {
|
|
2682
|
+
spinner.fail(`Agent #${tokenId} on chain ${issuingChainId} is owned by ${owner}, not ${virtualsWallet}`);
|
|
2683
|
+
process.exit(1);
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
const self = virtualsWallet.toLowerCase() === account.address.toLowerCase();
|
|
2687
|
+
const bindingMessage = buildBindingMessage(account.address, issuingChainId, tokenId);
|
|
2688
|
+
let binding;
|
|
2689
|
+
if (!self) {
|
|
2690
|
+
let bindingSig = opts.bindingSig;
|
|
2691
|
+
if (!bindingSig) {
|
|
2692
|
+
spinner.text = "Requesting binding signature from the economyOS wallet (acp wallet sign-message)...";
|
|
2693
|
+
bindingSig = await acpSignMessage(bindingMessage) ?? void 0;
|
|
2694
|
+
if (bindingSig) {
|
|
2695
|
+
console.log(chalk.dim(" Binding signed automatically via acp."));
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2698
|
+
if (!bindingSig) {
|
|
2699
|
+
spinner.fail("Binding signature required (economyOS wallet differs from your Sherwood wallet)");
|
|
2700
|
+
console.error();
|
|
2701
|
+
console.error(chalk.bold(" Sign this exact message with the economyOS agent wallet:"));
|
|
2702
|
+
console.error(chalk.green(` ${bindingMessage}`));
|
|
2703
|
+
console.error();
|
|
2704
|
+
console.error(chalk.dim(` acp wallet sign-message --message "${bindingMessage}" --json`));
|
|
2705
|
+
console.error(chalk.dim(" Then re-run with --binding-sig <signature>."));
|
|
2706
|
+
process.exit(1);
|
|
2707
|
+
}
|
|
2708
|
+
spinner.text = "Verifying binding signature...";
|
|
2709
|
+
const ok = await verifyBinding({
|
|
2710
|
+
sherwoodEoa: account.address,
|
|
2711
|
+
issuingChainId,
|
|
2712
|
+
tokenId,
|
|
2713
|
+
virtualsWallet,
|
|
2714
|
+
signature: bindingSig
|
|
2715
|
+
});
|
|
2716
|
+
if (!ok) {
|
|
2717
|
+
spinner.fail("Binding signature does not recover to the economyOS wallet");
|
|
2718
|
+
console.error(chalk.dim(` Expected signer: ${virtualsWallet}`));
|
|
2719
|
+
console.error(chalk.dim(` Signed message must be exactly: ${bindingMessage}`));
|
|
2720
|
+
process.exit(1);
|
|
2721
|
+
}
|
|
2722
|
+
binding = { sig: bindingSig, message: bindingMessage };
|
|
2723
|
+
}
|
|
2724
|
+
setVirtualsIdentity(Number(tokenId), issuingChainId, {
|
|
2725
|
+
agentId: virtualsAgentId,
|
|
2726
|
+
wallet: virtualsWallet,
|
|
2727
|
+
binding
|
|
2728
|
+
});
|
|
2729
|
+
spinner.succeed(
|
|
2730
|
+
opts.walletOnly ? "Virtuals identity linked: wallet-only (no ERC-8004 \u2014 upgrade later by re-linking after registration)" : `Virtuals identity linked: agent #${tokenId} (chain ${issuingChainId})`
|
|
2731
|
+
);
|
|
2732
|
+
console.log(chalk.dim(` economyOS wallet: ${virtualsWallet}`));
|
|
2733
|
+
if (virtualsAgentId) console.log(chalk.dim(` Virtuals agent: ${virtualsAgentId}`));
|
|
2734
|
+
console.log(chalk.dim(` Sherwood wallet: ${account.address}${self ? " (same as economyOS wallet \u2014 no binding needed)" : " (bound via signature)"}`));
|
|
2735
|
+
console.log(chalk.dim(` Saved to ~/.sherwood/config.json`));
|
|
2736
|
+
console.log();
|
|
2737
|
+
console.log(chalk.green("You can now create or join funds with this identity:"));
|
|
2738
|
+
console.log(chalk.dim(` sherwood fund create --agent-id ${tokenId} --subdomain <name> --name <name>`));
|
|
2739
|
+
} catch (err) {
|
|
2740
|
+
spinner.fail("Failed to link Virtuals identity");
|
|
2741
|
+
console.error(chalk.red(formatContractError(err)));
|
|
2742
|
+
process.exit(1);
|
|
2743
|
+
}
|
|
2744
|
+
});
|
|
2745
|
+
identity.command("find").description("Search ERC-8004 agents by wallet address or name (recover a lost agent ID)").option("--wallet <address>", "Filter by owner / wallet address").option("--name <text>", "Filter by agent name (exact or substring match, depending on registry)").option("--all-chains", "Search across all chains, not just the active one").action(async (opts) => {
|
|
2746
|
+
if (!opts.wallet && !opts.name) {
|
|
2747
|
+
console.error(chalk.red("Pass --wallet <address> or --name <text> (or both)."));
|
|
2748
|
+
process.exit(1);
|
|
2749
|
+
}
|
|
2750
|
+
const spinner = ora("Searching ERC-8004 registry...").start();
|
|
2751
|
+
try {
|
|
2752
|
+
const sdk = getAgent0SDK();
|
|
2753
|
+
const filters = {};
|
|
2754
|
+
if (opts.wallet) filters.walletAddress = opts.wallet;
|
|
2755
|
+
if (opts.name) filters.name = opts.name;
|
|
2756
|
+
filters.chains = opts.allChains ? "all" : [getChain().id];
|
|
2757
|
+
const results = await sdk.searchAgents(filters);
|
|
2758
|
+
spinner.stop();
|
|
2759
|
+
if (results.length === 0) {
|
|
2760
|
+
console.log(chalk.dim("No agents found matching those filters."));
|
|
2761
|
+
console.log(chalk.dim("If you minted on a different chain, retry with --all-chains."));
|
|
2762
|
+
return;
|
|
2763
|
+
}
|
|
2764
|
+
console.log();
|
|
2765
|
+
console.log(chalk.bold(`Found ${results.length} agent${results.length === 1 ? "" : "s"}`));
|
|
2766
|
+
console.log(chalk.dim("\u2500".repeat(50)));
|
|
2767
|
+
for (const a of results) {
|
|
2768
|
+
const idStr = String(a.agentId);
|
|
2769
|
+
const tokenId = idStr.includes(":") ? idStr.split(":")[1] : idStr;
|
|
2770
|
+
console.log(` ${chalk.bold(`#${tokenId}`)} ${a.name || chalk.dim("(no name)")}`);
|
|
2771
|
+
console.log(chalk.dim(` Chain: ${a.chainId}`));
|
|
2772
|
+
console.log(chalk.dim(` Full: ${idStr}`));
|
|
2773
|
+
if (a.owners?.length) {
|
|
2774
|
+
console.log(chalk.dim(` Owner: ${a.owners[0]}`));
|
|
2775
|
+
}
|
|
2776
|
+
if (a.walletAddress) {
|
|
2777
|
+
console.log(chalk.dim(` Wallet: ${a.walletAddress}`));
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
console.log();
|
|
2781
|
+
console.log(chalk.green("To bind one of these to this machine:"));
|
|
2782
|
+
console.log(chalk.dim(` sherwood identity load --id <tokenId>`));
|
|
2783
|
+
} catch (err) {
|
|
2784
|
+
spinner.fail("Search failed");
|
|
2785
|
+
console.error(chalk.red(formatContractError(err)));
|
|
2786
|
+
process.exit(1);
|
|
2787
|
+
}
|
|
2788
|
+
});
|
|
2789
|
+
identity.command("status").description("Show your agent identity status").action(async () => {
|
|
2790
|
+
const account = getAccount();
|
|
2791
|
+
if (getIdentityProvider() === "virtuals") {
|
|
2792
|
+
const virtuals = getVirtualsIdentity();
|
|
2793
|
+
const savedId = getAgentId();
|
|
2794
|
+
const issuingChainId = getIdentityChainId();
|
|
2795
|
+
console.log();
|
|
2796
|
+
console.log(chalk.bold("Agent Identity (ERC-8004 via Virtuals economyOS)"));
|
|
2797
|
+
console.log(chalk.dim("\u2500".repeat(40)));
|
|
2798
|
+
console.log(` Sherwood wallet: ${account.address}`);
|
|
2799
|
+
console.log(` economyOS wallet: ${virtuals?.wallet ?? chalk.red("(missing)")}`);
|
|
2800
|
+
if (virtuals?.agentId) console.log(` Virtuals agent: ${virtuals.agentId}`);
|
|
2801
|
+
const walletOnly = issuingChainId === WALLET_ONLY_CHAIN_ID;
|
|
2802
|
+
console.log(` Issuing chain: ${walletOnly ? chalk.yellow("wallet-only (no ERC-8004)") : issuingChainId ?? chalk.red("(missing)")}`);
|
|
2803
|
+
console.log(` Saved ID: ${savedId != null ? walletOnly ? chalk.dim("0 (wallet-only)") : `#${savedId}` : chalk.red("(missing)")}`);
|
|
2804
|
+
if (virtuals?.wallet && savedId != null && issuingChainId != null) {
|
|
2805
|
+
const spinner2 = ora("Re-verifying link...").start();
|
|
2806
|
+
try {
|
|
2807
|
+
let ownerOk = true;
|
|
2808
|
+
let owner;
|
|
2809
|
+
if (!walletOnly) {
|
|
2810
|
+
owner = await ownerOfOnIssuingChain(issuingChainId, BigInt(savedId));
|
|
2811
|
+
ownerOk = owner.toLowerCase() === virtuals.wallet.toLowerCase();
|
|
2812
|
+
}
|
|
2813
|
+
let bindingOk = virtuals.wallet.toLowerCase() === account.address.toLowerCase();
|
|
2814
|
+
if (!bindingOk && virtuals.binding?.sig) {
|
|
2815
|
+
bindingOk = await verifyBinding({
|
|
2816
|
+
sherwoodEoa: account.address,
|
|
2817
|
+
issuingChainId,
|
|
2818
|
+
tokenId: BigInt(savedId),
|
|
2819
|
+
virtualsWallet: virtuals.wallet,
|
|
2820
|
+
signature: virtuals.binding.sig
|
|
2821
|
+
});
|
|
2822
|
+
}
|
|
2823
|
+
spinner2.stop();
|
|
2824
|
+
if (walletOnly) {
|
|
2825
|
+
console.log(` NFT ownership: ${chalk.dim("n/a (wallet-only link)")}`);
|
|
2826
|
+
} else {
|
|
2827
|
+
console.log(` NFT ownership: ${ownerOk ? chalk.green("verified") : chalk.red(`FAILED (owner is ${owner})`)}`);
|
|
2828
|
+
}
|
|
2829
|
+
console.log(` Binding: ${bindingOk ? chalk.green("verified") : chalk.red("FAILED")}`);
|
|
2830
|
+
if (!ownerOk) {
|
|
2831
|
+
console.log(chalk.yellow(" The NFT moved since linking \u2014 re-run 'sherwood identity link-virtuals'."));
|
|
2832
|
+
}
|
|
2833
|
+
if (walletOnly) {
|
|
2834
|
+
console.log(chalk.dim(" Upgrade to a registered link once ERC-8004 registration is available:"));
|
|
2835
|
+
console.log(chalk.dim(" acp agent register-erc8004 ... && sherwood identity link-virtuals"));
|
|
2836
|
+
}
|
|
2837
|
+
} catch (err) {
|
|
2838
|
+
spinner2.warn("Could not re-verify against the issuing chain");
|
|
2839
|
+
console.log(chalk.dim(` ${err.message}`));
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
console.log();
|
|
2843
|
+
return;
|
|
2844
|
+
}
|
|
2845
|
+
const registry = AGENT_REGISTRY().IDENTITY_REGISTRY;
|
|
2846
|
+
const client = getPublicClient();
|
|
2847
|
+
const spinner = ora("Checking identity...").start();
|
|
2848
|
+
try {
|
|
2849
|
+
const balance = await client.readContract({
|
|
2850
|
+
address: registry,
|
|
2851
|
+
abi: IDENTITY_REGISTRY_ABI2,
|
|
2852
|
+
functionName: "balanceOf",
|
|
2853
|
+
args: [account.address]
|
|
2854
|
+
});
|
|
2855
|
+
spinner.stop();
|
|
2856
|
+
const savedId = getAgentId();
|
|
2857
|
+
console.log();
|
|
2858
|
+
console.log(chalk.bold("Agent Identity (ERC-8004)"));
|
|
2859
|
+
console.log(chalk.dim("\u2500".repeat(40)));
|
|
2860
|
+
console.log(` Wallet: ${account.address}`);
|
|
2861
|
+
console.log(` Registry: ${registry}`);
|
|
2862
|
+
console.log(` NFTs owned: ${balance.toString()}`);
|
|
2863
|
+
if (savedId) {
|
|
2864
|
+
try {
|
|
2865
|
+
const owner = await client.readContract({
|
|
2866
|
+
address: registry,
|
|
2867
|
+
abi: IDENTITY_REGISTRY_ABI2,
|
|
2868
|
+
functionName: "ownerOf",
|
|
2869
|
+
args: [BigInt(savedId)]
|
|
2870
|
+
});
|
|
2871
|
+
const isOwner = owner.toLowerCase() === account.address.toLowerCase();
|
|
2872
|
+
console.log(` Saved ID: #${savedId} ${isOwner ? chalk.green("(verified)") : chalk.red("(owned by " + owner + ")")}`);
|
|
2873
|
+
if (isOwner) {
|
|
2874
|
+
try {
|
|
2875
|
+
const sdk = getAgent0SDK();
|
|
2876
|
+
const agent = await sdk.loadAgent(`${getChain().id}:${savedId}`);
|
|
2877
|
+
if (agent.name) console.log(` Name: ${agent.name}`);
|
|
2878
|
+
if (agent.description) console.log(` Desc: ${chalk.dim(agent.description)}`);
|
|
2879
|
+
if (agent.walletAddress) console.log(` Wallet: ${agent.walletAddress}`);
|
|
2880
|
+
} catch {
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2883
|
+
} catch {
|
|
2884
|
+
console.log(` Saved ID: #${savedId} ${chalk.red("(token not found)")}`);
|
|
2885
|
+
}
|
|
2886
|
+
console.log();
|
|
2887
|
+
} else if (balance > 0n) {
|
|
2888
|
+
console.log(` Saved ID: ${chalk.yellow("none (but this wallet owns NFTs \u2014 listing below)")}`);
|
|
2889
|
+
console.log();
|
|
2890
|
+
const lookupSpinner = ora("Resolving owned token IDs via Agent0 SDK...").start();
|
|
2891
|
+
try {
|
|
2892
|
+
const sdk = getAgent0SDK();
|
|
2893
|
+
const results = await sdk.searchAgents({
|
|
2894
|
+
walletAddress: account.address,
|
|
2895
|
+
chains: [getChain().id]
|
|
2896
|
+
});
|
|
2897
|
+
lookupSpinner.stop();
|
|
2898
|
+
if (results.length === 0) {
|
|
2899
|
+
console.log(chalk.dim(" No agents resolved for this wallet on the active chain."));
|
|
2900
|
+
console.log(chalk.dim(" If you minted on a different chain, try:"));
|
|
2901
|
+
console.log(chalk.dim(" sherwood identity find --wallet " + account.address + " --all-chains"));
|
|
2902
|
+
} else {
|
|
2903
|
+
console.log(chalk.bold(` Owned identities on this chain (${results.length}):`));
|
|
2904
|
+
for (const a of results) {
|
|
2905
|
+
const idStr = String(a.agentId);
|
|
2906
|
+
const tokenId = idStr.includes(":") ? idStr.split(":")[1] : idStr;
|
|
2907
|
+
console.log(` ${chalk.bold(`#${tokenId}`)} ${a.name || chalk.dim("(no name)")}`);
|
|
2908
|
+
}
|
|
2909
|
+
console.log();
|
|
2910
|
+
console.log(chalk.green(" Bind one to this machine:"));
|
|
2911
|
+
if (results.length === 1) {
|
|
2912
|
+
const idStr = String(results[0].agentId);
|
|
2913
|
+
const tokenId = idStr.includes(":") ? idStr.split(":")[1] : idStr;
|
|
2914
|
+
console.log(chalk.dim(` sherwood identity load --id ${tokenId}`));
|
|
2915
|
+
} else {
|
|
2916
|
+
console.log(chalk.dim(" sherwood identity load --id <tokenId>"));
|
|
2917
|
+
}
|
|
2918
|
+
}
|
|
2919
|
+
} catch (err) {
|
|
2920
|
+
lookupSpinner.warn("Could not resolve owned token IDs via Agent0 SDK");
|
|
2921
|
+
console.log(chalk.dim(` ${err.message}`));
|
|
2922
|
+
console.log(chalk.dim(" Try: sherwood identity find --wallet " + account.address));
|
|
2923
|
+
}
|
|
2924
|
+
console.log();
|
|
2925
|
+
} else {
|
|
2926
|
+
console.log(` Saved ID: ${chalk.dim("none \u2014 run 'sherwood identity mint --name <name>'")}`);
|
|
2927
|
+
console.log();
|
|
2928
|
+
}
|
|
2929
|
+
} catch (err) {
|
|
2930
|
+
spinner.fail("Failed to check identity");
|
|
2931
|
+
console.error(chalk.red(formatContractError(err)));
|
|
2932
|
+
process.exit(1);
|
|
2933
|
+
}
|
|
2934
|
+
});
|
|
2935
|
+
}
|
|
2936
|
+
|
|
2937
|
+
export {
|
|
2938
|
+
setCalldataOnly,
|
|
2939
|
+
isCalldataOnly,
|
|
2940
|
+
emitCalldata,
|
|
2941
|
+
encodeDeposit,
|
|
2942
|
+
encodeRedeem,
|
|
2943
|
+
encodeApproveDepositor,
|
|
2944
|
+
encodeRegisterAgent,
|
|
2945
|
+
encodeVote,
|
|
2946
|
+
encodeExecute,
|
|
2947
|
+
encodeSettle,
|
|
2948
|
+
encodeCancel,
|
|
2949
|
+
encodePropose,
|
|
2950
|
+
encodeProposeWithSandbox,
|
|
2951
|
+
encodeUnstick,
|
|
2952
|
+
encodeEmergencySettle,
|
|
2953
|
+
encodeGovernorParam,
|
|
2954
|
+
encodeProtocolFeeBps,
|
|
2955
|
+
encodeCreateFund,
|
|
2956
|
+
encodeStrategyDeploy,
|
|
2957
|
+
encodeJoinRequest,
|
|
2958
|
+
encodeApproveAgent,
|
|
2959
|
+
encodeGuardianStake,
|
|
2960
|
+
encodeGuardianUnstake,
|
|
2961
|
+
encodeVoteOnProposal,
|
|
2962
|
+
DEFAULT_ISSUING_CHAIN_ID,
|
|
2963
|
+
appendVidTag,
|
|
2964
|
+
parseVidTag,
|
|
2965
|
+
stripVidTag,
|
|
2966
|
+
verifyVidTag,
|
|
2967
|
+
makeOwnerOfCache,
|
|
2968
|
+
lookupAgentIdByWallet,
|
|
2969
|
+
registerIdentityCommands
|
|
2970
|
+
};
|
|
2971
|
+
//# sourceMappingURL=chunk-ILHOW6IA.js.map
|