forked from NSPG13/agent-bounties
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathautonomous-activation.js
More file actions
384 lines (356 loc) · 16.8 KB
/
Copy pathautonomous-activation.js
File metadata and controls
384 lines (356 loc) · 16.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
(() => {
"use strict";
const BASE_CHAIN_ID = "0x2105";
const BUNDLE_URL = "/deployments/canonical-child-seeds-base-mainnet.json";
const VERIFIER_BUNDLE_URL = "/deployments/canonical-child-verifier-base-mainnet-deployment.json";
const VERIFIER_MODULE = "0x40adac5a1d00a725f77682f8940b893eaed31ecf";
const ACCEPTANCE_CRITERIA_HASH = "0xa103c2c907f96e03a2f2b0e6b2209e0a3ca53686f7e9f79d89d7bfa1f8e314de";
const EXPECTED_ISSUES = [217, 218, 219, 220];
const state = { bundle: null, account: null, provider: null, providers: [], pendingBounties: [], inspected: false };
const announcedProviders = [];
const byId = (id) => document.getElementById(id);
const sleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
function write(target, value, tone = "") {
target.textContent = Array.isArray(value) ? value.join("\n") : value;
target.dataset.tone = tone;
}
function requireLocalOrigin() {
if (!new Set(["127.0.0.1", "localhost"]).has(location.hostname)) {
throw new Error("Activation console must be served from localhost.");
}
}
async function loadBundle() {
requireLocalOrigin();
const [response, verifierResponse] = await Promise.all([
fetch(BUNDLE_URL, { cache: "no-store" }),
fetch(VERIFIER_BUNDLE_URL, { cache: "no-store" }),
]);
if (!response.ok || !verifierResponse.ok) throw new Error("A checked-in activation artifact is unavailable.");
const bundle = await response.json();
const verifierBundle = await verifierResponse.json();
if (
bundle.schema_version !== "agent-bounties/autonomous-activation-bundle-v1"
|| bundle.network !== "base-mainnet"
|| bundle.chain_id !== 8453
|| bundle.manifest_canonical_json_keccak256 !== "0x5247f873889a63c273ec0531137c7723463d9943251b32c49b0843e39433c3b6"
|| bundle.creation_batch.total_initial_funding !== "4000000"
|| bundle.bounties.length !== 4
|| bundle.creation_batch.wallet_calls.length !== 5
|| bundle.bounties.some((item, index) => item.issue !== EXPECTED_ISSUES[index])
|| verifierBundle.schema_version !== "agent-bounties/canonical-child-verifier-deployment-v1"
|| verifierBundle.deployment.expected_contract !== VERIFIER_MODULE
|| verifierBundle.acceptance_criteria_hash !== ACCEPTANCE_CRITERIA_HASH
) {
throw new Error("Activation artifacts violate the locked canonical-child-v1 contract.");
}
state.bundle = bundle;
state.verifierBundle = verifierBundle;
document.querySelector("[data-factory]").textContent = bundle.deployment.expected_factory;
document.querySelector("[data-implementation]").textContent = bundle.deployment.expected_implementation;
document.querySelector("[data-creator]").textContent = bundle.deployment.from;
document.querySelector("[data-bounties]").textContent = bundle.bounties.map((item) => `#${item.issue}`).join(", ");
document.querySelector("[data-funding]").textContent = `${Number(bundle.creation_batch.total_initial_funding) / 1_000_000} USDC`;
return bundle;
}
function isWalletProvider(provider) {
return Boolean(provider && typeof provider.request === "function");
}
function providerName(provider, info = {}) {
if (info.name) return info.name;
if (provider.isMetaMask) return "MetaMask";
if (provider.isCoinbaseWallet) return "Coinbase Wallet";
if (provider.isBraveWallet) return "Brave Wallet";
return "Injected wallet";
}
function rememberProvider(event) {
const detail = event && event.detail;
if (!detail || !detail.provider || announcedProviders.some((item) => item.provider === detail.provider)) return;
announcedProviders.push(detail);
}
window.addEventListener("eip6963:announceProvider", rememberProvider);
async function discoverProviders() {
window.dispatchEvent(new Event("eip6963:requestProvider"));
await sleep(500);
const candidates = [...announcedProviders];
const injected = window.ethereum && Array.isArray(window.ethereum.providers)
? window.ethereum.providers
: (window.ethereum ? [window.ethereum] : []);
for (const provider of injected) {
if (isWalletProvider(provider) && !candidates.some((item) => item.provider === provider)) {
candidates.push({ provider, info: {} });
}
}
state.providers = candidates.filter((item) => isWalletProvider(item.provider));
const selector = byId("wallet-provider");
selector.replaceChildren(...state.providers.map((item, index) => {
const option = document.createElement("option");
option.value = String(index);
option.textContent = providerName(item.provider, item.info);
return option;
}));
selector.disabled = state.providers.length === 0;
if (state.providers.length === 0) {
throw new Error("No EIP-1193 wallet is exposed to this page. Unlock a browser wallet and reload.");
}
}
function selectedProvider() {
const item = state.providers[Number.parseInt(byId("wallet-provider").value, 10)];
if (!item) throw new Error("Select an available wallet provider.");
state.provider = item.provider;
return item;
}
async function wallet(method, params = []) {
const provider = state.provider || selectedProvider().provider;
return provider.request({ method, params });
}
async function connect() {
const accounts = await wallet("eth_requestAccounts");
if (!accounts || !accounts[0]) throw new Error("The wallet returned no account.");
const account = accounts[0].toLowerCase();
if (account !== state.bundle.deployment.from.toLowerCase()) {
throw new Error(`Select the committed creator wallet ${state.bundle.deployment.from}.`);
}
if ((await wallet("eth_chainId")).toLowerCase() !== BASE_CHAIN_ID) {
await wallet("wallet_switchEthereumChain", [{ chainId: BASE_CHAIN_ID }]);
}
state.account = account;
return account;
}
function addressWord(address) {
return address.toLowerCase().replace(/^0x/, "").padStart(64, "0");
}
function uintResult(value) {
return BigInt(value || "0x0");
}
function addressResult(value) {
return `0x${String(value).replace(/^0x/, "").slice(-40)}`.toLowerCase();
}
async function call(to, data) {
return wallet("eth_call", [{ to, data }, "latest"]);
}
async function tokenBalance(address) {
return uintResult(await call(state.bundle.deployment.settlement_token, `0x70a08231${addressWord(address)}`));
}
async function verifyFactory() {
const deployment = state.bundle.deployment;
const code = await wallet("eth_getCode", [deployment.expected_factory, "latest"]);
if (!code || code === "0x") return false;
const implementation = addressResult(await call(deployment.expected_factory, "0x5c60da1b"));
const token = addressResult(await call(deployment.expected_factory, "0x7b9e618d"));
if (implementation !== deployment.expected_implementation.toLowerCase()) {
throw new Error(`Factory implementation mismatch: ${implementation}`);
}
if (token !== deployment.settlement_token.toLowerCase()) {
throw new Error(`Factory settlement token mismatch: ${token}`);
}
return true;
}
async function verifyVerifierModule() {
const deployment = state.verifierBundle.deployment;
const code = (await wallet("eth_getCode", [VERIFIER_MODULE, "latest"])).toLowerCase();
if (!code || code === "0x") throw new Error("Deploy the canonical child verifier before funding bounties.");
if (code !== deployment.expected_runtime_code) throw new Error("Canonical child verifier runtime bytecode mismatch.");
const factory = addressResult(await call(VERIFIER_MODULE, "0x044f3e72"));
const token = addressResult(await call(VERIFIER_MODULE, "0x7b9e618d"));
const criteria = (await call(VERIFIER_MODULE, "0x77de6ca7")).toLowerCase();
if (
factory !== state.bundle.deployment.expected_factory.toLowerCase()
|| token !== state.bundle.deployment.settlement_token.toLowerCase()
|| criteria !== ACCEPTANCE_CRITERIA_HASH
) {
throw new Error("Canonical child verifier immutable configuration mismatch.");
}
}
async function bountyIsActivated(bounty) {
const contract = bounty.predicted_bounty_contract;
const code = await wallet("eth_getCode", [contract, "latest"]);
if (!code || code === "0x") return false;
const canonical = uintResult(await call(state.bundle.deployment.expected_factory, `0xdb021126${addressWord(contract)}`));
const bountyId = (await call(contract, "0xc17bd75e")).toLowerCase();
const funded = uintResult(await call(contract, "0x820a5f50"));
const target = uintResult(await call(contract, "0x953b8fb8"));
const status = uintResult(await call(contract, "0x200d2ed2"));
const balance = await tokenBalance(contract);
const verifier = addressResult(await call(contract, "0x41506fc1"));
const criteria = (await call(contract, "0x8a2b02be")).toLowerCase();
const terms = (await call(contract, "0xb311d9fd")).toLowerCase();
if (
canonical !== 1n
|| bountyId !== bounty.bounty_id.toLowerCase()
|| funded !== 1_000_000n
|| target !== 1_000_000n
|| balance !== 1_000_000n
|| status !== 1n
|| verifier !== VERIFIER_MODULE
|| criteria !== ACCEPTANCE_CRITERIA_HASH
|| terms !== bounty.commitments.terms_hash.toLowerCase()
) {
throw new Error(`Issue #${bounty.issue} exists but fails the locked canonical funding contract.`);
}
return true;
}
async function inspect() {
const target = byId("inspect-output");
try {
const account = await connect();
const nonce = Number.parseInt(await wallet("eth_getTransactionCount", [account, "latest"]), 16);
const eth = uintResult(await wallet("eth_getBalance", [account, "latest"]));
const usdc = await tokenBalance(account);
const factoryExists = await verifyFactory();
if (!factoryExists) throw new Error("The attested canonical factory is unavailable.");
await verifyVerifierModule();
const pendingBounties = [];
for (const bounty of state.bundle.bounties) {
if (!(await bountyIsActivated(bounty))) pendingBounties.push(bounty);
}
const requiredFunding = BigInt(pendingBounties.length) * 1_000_000n;
if (usdc < requiredFunding) {
throw new Error(`Wallet has ${Number(usdc) / 1_000_000} USDC; ${Number(requiredFunding) / 1_000_000} USDC is required for the remaining bounties.`);
}
state.pendingBounties = pendingBounties;
state.inspected = true;
byId("activate").disabled = pendingBounties.length !== state.bundle.bounties.length;
byId("sequential").hidden = pendingBounties.length === 0 || pendingBounties.length === state.bundle.bounties.length;
byId("sequential").disabled = pendingBounties.length === 0;
byId("verify").disabled = false;
write(target, [
`Wallet provider: ${providerName(state.provider)}`,
`Account: ${account}`,
`Chain: Base mainnet (${BASE_CHAIN_ID})`,
`Nonce: ${nonce}`,
`ETH: ${(Number(eth) / 1e18).toFixed(6)}`,
`USDC: ${(Number(usdc) / 1_000_000).toFixed(6)}`,
"Factory: deployed and immutable configuration verified",
"Verifier: deployed and byte-for-byte verified",
pendingBounties.length === 0
? "Bounties: all four are deployed; verify canonical state"
: `Bounties: ${pendingBounties.length} of 4 remain; ${Number(requiredFunding) / 1_000_000} USDC required`,
], "success");
} catch (error) {
state.inspected = false;
state.pendingBounties = [];
byId("activate").disabled = true;
byId("sequential").disabled = true;
byId("sequential").hidden = true;
byId("verify").disabled = true;
write(target, error.message || String(error), "error");
}
}
async function waitReceipt(transactionHash, timeoutMilliseconds = 180_000) {
const deadline = Date.now() + timeoutMilliseconds;
while (Date.now() < deadline) {
const receipt = await wallet("eth_getTransactionReceipt", [transactionHash]);
if (receipt) {
if (receipt.status !== "0x1") throw new Error(`Transaction reverted: ${transactionHash}`);
return receipt;
}
await sleep(1_500);
}
throw new Error(`Transaction confirmation timed out: ${transactionHash}`);
}
async function verifyActivation(timeoutMilliseconds = 0) {
const deadline = Date.now() + timeoutMilliseconds;
do {
try {
if (!(await verifyFactory())) throw new Error("Canonical factory is not deployed.");
await verifyVerifierModule();
const results = [];
for (const bounty of state.bundle.bounties) {
if (!(await bountyIsActivated(bounty))) {
throw new Error(`Issue #${bounty.issue} is not yet canonical, fully funded, and claimable.`);
}
results.push(`#${bounty.issue}: ${bounty.predicted_bounty_contract} | 1 USDC | claimable`);
}
return results;
} catch (error) {
if (Date.now() >= deadline) throw error;
await sleep(2_000);
}
} while (true);
}
async function showVerifiedActivation(timeoutMilliseconds = 0) {
const target = byId("activate-output");
try {
const results = await verifyActivation(timeoutMilliseconds);
write(target, ["Canonical activation verified from chain state.", ...results, "Indexer reconciliation is still required before hosted funded/claimable language."], "success");
} catch (error) {
write(target, error.message || String(error), "error");
}
}
async function activateBatch() {
const target = byId("activate-output");
try {
await inspect();
if (!state.inspected) return;
if (state.pendingBounties.length !== state.bundle.bounties.length) {
throw new Error("Atomic activation is available only before any seed bounty exists. Use the bounded sequential recovery path.");
}
write(target, "Wallet confirmation requested for one exact five-call batch.");
await wallet("wallet_sendCalls", [{
version: "2.0.0",
chainId: BASE_CHAIN_ID,
from: state.account,
atomicRequired: true,
calls: state.bundle.creation_batch.wallet_calls.map((item) => ({ to: item.to, data: item.data, value: "0x0" })),
}]);
await showVerifiedActivation(180_000);
} catch (error) {
byId("sequential").hidden = false;
byId("sequential").disabled = false;
write(target, [`Wallet batch was not completed: ${error.message || String(error)}`, "Use the explicit sequential fallback only if the wallet does not support EIP-5792 batching."], "error");
}
}
async function activateSequential() {
const target = byId("activate-output");
byId("sequential").disabled = true;
try {
await inspect();
if (!state.inspected || state.pendingBounties.length === 0) return;
const approvalTemplate = state.bundle.creation_batch.wallet_calls[0];
const remainingFunding = BigInt(state.pendingBounties.length) * 1_000_000n;
const approvalData = `${approvalTemplate.data.slice(0, -64)}${remainingFunding.toString(16).padStart(64, "0")}`;
const transactions = [{ ...approvalTemplate, data: approvalData }, ...state.pendingBounties.map((bounty) => {
const index = state.bundle.bounties.findIndex((item) => item.issue === bounty.issue);
return state.bundle.creation_batch.wallet_calls[index + 1];
})];
for (const transaction of transactions) {
write(target, `Wallet confirmation requested: ${transaction.function}`);
const hash = await wallet("eth_sendTransaction", [{ from: state.account, to: transaction.to, data: transaction.data, value: "0x0" }]);
await waitReceipt(hash);
}
await showVerifiedActivation(90_000);
} catch (error) {
byId("sequential").disabled = false;
write(target, error.message || String(error), "error");
}
}
async function initialize() {
try {
await loadBundle();
} catch (error) {
write(byId("inspect-output"), error.message || String(error), "error");
byId("inspect").disabled = true;
return;
}
try {
await discoverProviders();
} catch (error) {
write(byId("inspect-output"), error.message || String(error), "error");
}
byId("wallet-provider").addEventListener("change", () => {
state.provider = null;
state.account = null;
state.pendingBounties = [];
state.inspected = false;
byId("activate").disabled = true;
byId("sequential").disabled = true;
byId("sequential").hidden = true;
byId("verify").disabled = true;
});
byId("inspect").addEventListener("click", inspect);
byId("activate").addEventListener("click", activateBatch);
byId("sequential").addEventListener("click", activateSequential);
byId("verify").addEventListener("click", () => showVerifiedActivation());
}
document.addEventListener("DOMContentLoaded", initialize);
})();