@dev.sail.money/sailor 0.0.2-22 → 0.0.2-24
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/examples/permissions/BoundedSupply_AaveV3_Arbitrum.sol +6 -8
- package/examples/permissions/SailCalldata.sol +118 -0
- package/package.json +1 -1
- package/packages/cli/dist/index.cjs +248 -28
- package/packages/cli/dist/server.cjs +56 -16
- package/packages/sdk/dist/index.d.ts +1 -1
- package/packages/sdk/dist/index.d.ts.map +1 -1
- package/packages/sdk/dist/index.js +1 -1
- package/packages/sdk/dist/index.js.map +1 -1
- package/packages/sdk/dist/intelligence.d.ts +1 -1
- package/packages/sdk/dist/intelligence.js +1 -1
- package/packages/sdk/dist/lifi.d.ts +17 -0
- package/packages/sdk/dist/lifi.d.ts.map +1 -1
- package/packages/sdk/dist/lifi.js +24 -0
- package/packages/sdk/dist/lifi.js.map +1 -1
- package/packages/sdk/dist/types.d.ts +17 -1
- package/packages/sdk/dist/types.d.ts.map +1 -1
- package/packages/ui/dist/assets/{add-C--RBwJe.js → add--OaWHMEX.js} +1 -1
- package/packages/ui/dist/assets/{all-wallets-_xwd_eso.js → all-wallets-BH_4qsJ0.js} +1 -1
- package/packages/ui/dist/assets/{app-store-CIQsK1zU.js → app-store-j8XNWdo_.js} +1 -1
- package/packages/ui/dist/assets/{apple-BdlAnnmO.js → apple-DoNsugim.js} +1 -1
- package/packages/ui/dist/assets/{arrow-bottom-B5p_6Dat.js → arrow-bottom-D_enDpNq.js} +1 -1
- package/packages/ui/dist/assets/{arrow-bottom-circle-D7c6JPTF.js → arrow-bottom-circle-WWFGXKiz.js} +1 -1
- package/packages/ui/dist/assets/{arrow-left-SA4NpEnP.js → arrow-left-BUJGpX55.js} +1 -1
- package/packages/ui/dist/assets/{arrow-right-mOJNWujS.js → arrow-right-D5mkI_SK.js} +1 -1
- package/packages/ui/dist/assets/{arrow-top-CvPVVpHl.js → arrow-top-BXhKZNjN.js} +1 -1
- package/packages/ui/dist/assets/{bank-B2j2rPm9.js → bank-Dkkf0tum.js} +1 -1
- package/packages/ui/dist/assets/{basic-Bw6cXOlk.js → basic-DOdQ4iGr.js} +1 -1
- package/packages/ui/dist/assets/{browser-CUSNF__N.js → browser-NOeeokaH.js} +1 -1
- package/packages/ui/dist/assets/{card-CpKLox49.js → card-cdDhvSTA.js} +1 -1
- package/packages/ui/dist/assets/{ccip-XB9iQjXB.js → ccip-Dsn_0RCo.js} +1 -1
- package/packages/ui/dist/assets/{checkmark-BRpXeSCK.js → checkmark-9fzIA8S7.js} +1 -1
- package/packages/ui/dist/assets/{checkmark-bold-BkPvoqxo.js → checkmark-bold-DDvnKkth.js} +1 -1
- package/packages/ui/dist/assets/{chevron-bottom-CtK0W2av.js → chevron-bottom-CZ0cAj9w.js} +1 -1
- package/packages/ui/dist/assets/{chevron-left-NayfPMDy.js → chevron-left-cbciRp76.js} +1 -1
- package/packages/ui/dist/assets/{chevron-right-BPU2hCfA.js → chevron-right-aaIM8CMM.js} +1 -1
- package/packages/ui/dist/assets/{chevron-top-CTXwC4nM.js → chevron-top-Cv9x0gjk.js} +1 -1
- package/packages/ui/dist/assets/{chrome-store-eWIk0-YZ.js → chrome-store-DlOKVEJe.js} +1 -1
- package/packages/ui/dist/assets/{clock-VmYiq5jB.js → clock-5QviMwVt.js} +1 -1
- package/packages/ui/dist/assets/{close-NfBukMzW.js → close-myWpcxkH.js} +1 -1
- package/packages/ui/dist/assets/{coinPlaceholder-BWOeJc6j.js → coinPlaceholder-D3UzHxQZ.js} +1 -1
- package/packages/ui/dist/assets/{compass-oRk8W3iM.js → compass-DN7a9rAJ.js} +1 -1
- package/packages/ui/dist/assets/{copy-GcYQZOsF.js → copy-5dhhqdUd.js} +1 -1
- package/packages/ui/dist/assets/{core-B_rvnvkC.js → core-BlknpGLl.js} +3 -3
- package/packages/ui/dist/assets/cursor-Ckhq1uuc.js +3 -0
- package/packages/ui/dist/assets/{cursor-transparent-CGox3wZ-.js → cursor-transparent-hWLSc0KZ.js} +1 -1
- package/packages/ui/dist/assets/{desktop-DU4yyiV4.js → desktop-CZ-Yyu9E.js} +1 -1
- package/packages/ui/dist/assets/{disconnect-CJm9NnxK.js → disconnect-BkFpHyPA.js} +1 -1
- package/packages/ui/dist/assets/{discord-MxDL8Eq6.js → discord-BQr8vCO7.js} +1 -1
- package/packages/ui/dist/assets/{etherscan-CkCvlZiA.js → etherscan-BRQnPWan.js} +1 -1
- package/packages/ui/dist/assets/{events-CkyJn32_.js → events-BPX61gpp.js} +1 -1
- package/packages/ui/dist/assets/{exclamation-triangle-hH1JdYAZ.js → exclamation-triangle-DgiBPDRx.js} +1 -1
- package/packages/ui/dist/assets/{extension-DTMrXG5m.js → extension-DqOhrhYM.js} +1 -1
- package/packages/ui/dist/assets/{external-link-GSwn5MzD.js → external-link-CN3oX5O9.js} +1 -1
- package/packages/ui/dist/assets/{facebook-Vw_uyzaE.js → facebook-Y4EwFoke.js} +1 -1
- package/packages/ui/dist/assets/{fallback-BL3U4ZRT.js → fallback-DTghxJb4.js} +1 -1
- package/packages/ui/dist/assets/{farcaster-F-_di36M.js → farcaster-Bk5F07SC.js} +1 -1
- package/packages/ui/dist/assets/{filters-DQzcstDl.js → filters-Cz-QDvgT.js} +1 -1
- package/packages/ui/dist/assets/{github-BSq3_rEd.js → github-Cxkp89Tq.js} +1 -1
- package/packages/ui/dist/assets/{google-BU4QXiDS.js → google-DYqw-KYU.js} +1 -1
- package/packages/ui/dist/assets/{help-circle-CuF4iPyF.js → help-circle-BXMNYoIA.js} +1 -1
- package/packages/ui/dist/assets/{id-BQWlv0a_.js → id-BaD71KYD.js} +1 -1
- package/packages/ui/dist/assets/{image-BPNySDPo.js → image-B23hGUdW.js} +1 -1
- package/packages/ui/dist/assets/{index-D2wgBslE.js → index-BN6XqPDp.js} +1 -1
- package/packages/ui/dist/assets/{index-CMyY4FOR.js → index-BQ4JZ1HB.js} +3 -3
- package/packages/ui/dist/assets/{index-BMPQOOgv.js → index-C3IX4alt.js} +1 -1
- package/packages/ui/dist/assets/{index-Dc9_WV0G.js → index-CL1Pp-W8.js} +77 -77
- package/packages/ui/dist/assets/index-DCnJ64lX.css +1 -0
- package/packages/ui/dist/assets/{index-CsbiKM3b.js → index-DXwK5tlD.js} +1 -1
- package/packages/ui/dist/assets/{index-D0SPxlSM.js → index-Diq2KQvQ.js} +1 -1
- package/packages/ui/dist/assets/{index.es-CvyDIsY4.js → index.es-5g6Py2iz.js} +4 -4
- package/packages/ui/dist/assets/{info-D20yslek.js → info-DXuKXV9d.js} +1 -1
- package/packages/ui/dist/assets/{info-circle-BEjvYTHa.js → info-circle-CYkuEbJC.js} +1 -1
- package/packages/ui/dist/assets/{lightbulb-DfvLi5mQ.js → lightbulb-CuI54_aI.js} +1 -1
- package/packages/ui/dist/assets/{mail-CkgaIJAd.js → mail--8E_kt-f.js} +1 -1
- package/packages/ui/dist/assets/{metamask-sdk-O-IBvvGq.js → metamask-sdk-yzZWWcs3.js} +1 -1
- package/packages/ui/dist/assets/{mobile-CGc88WfG.js → mobile-CZymO9zO.js} +1 -1
- package/packages/ui/dist/assets/{more-DnX8wlTn.js → more-BYAwsmOZ.js} +1 -1
- package/packages/ui/dist/assets/{network-placeholder-DDrgA4a3.js → network-placeholder-08UzyBww.js} +1 -1
- package/packages/ui/dist/assets/{nftPlaceholder-DhHWPuD3.js → nftPlaceholder-BYQcom9C.js} +1 -1
- package/packages/ui/dist/assets/{off-D1CsYvPQ.js → off-CYMrTsdm.js} +1 -1
- package/packages/ui/dist/assets/{parseSignature-BlZUbtEc.js → parseSignature-WNjhiGa-.js} +1 -1
- package/packages/ui/dist/assets/{play-store-Dbkk8PTZ.js → play-store-C8qwnge4.js} +1 -1
- package/packages/ui/dist/assets/{plus-B8jXpls3.js → plus-D44RqQir.js} +1 -1
- package/packages/ui/dist/assets/{qr-code-CDuJ3ftj.js → qr-code-B2Zo3ucm.js} +1 -1
- package/packages/ui/dist/assets/{recycle-horizontal-ZFGjaHsZ.js → recycle-horizontal-GWYpxQB9.js} +1 -1
- package/packages/ui/dist/assets/{refresh-D0rMEDtF.js → refresh-MgpEHHa3.js} +1 -1
- package/packages/ui/dist/assets/{reown-logo-NlCNVmgd.js → reown-logo-CteGf0y0.js} +1 -1
- package/packages/ui/dist/assets/{search-CrJAA2qW.js → search-tUzIsCFS.js} +1 -1
- package/packages/ui/dist/assets/{secp256k1-mJj6W2AI.js → secp256k1-DmFqeUJ_.js} +1 -1
- package/packages/ui/dist/assets/{send-C7CoRziM.js → send-C9UNMMEg.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontal-fD3wbCGJ.js → swapHorizontal-Bhb2KCgj.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalBold-Cc-jQ6as.js → swapHorizontalBold-Dx8XHsp8.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalMedium-DlJW6uX1.js → swapHorizontalMedium-BU5Bg66C.js} +1 -1
- package/packages/ui/dist/assets/{swapHorizontalRoundedBold-1VHOerLO.js → swapHorizontalRoundedBold-DZtiRbnr.js} +1 -1
- package/packages/ui/dist/assets/{swapVertical-CKaRlkZK.js → swapVertical-Da4jr_Iy.js} +1 -1
- package/packages/ui/dist/assets/{telegram-DnCYed4D.js → telegram-2JwKMtW5.js} +1 -1
- package/packages/ui/dist/assets/{three-dots-BFluoxma.js → three-dots-BvVnpuAg.js} +1 -1
- package/packages/ui/dist/assets/{twitch-BXGv98S9.js → twitch-C6DOrjYX.js} +1 -1
- package/packages/ui/dist/assets/{twitterIcon-C6IdXEe5.js → twitterIcon-BQu41-nP.js} +1 -1
- package/packages/ui/dist/assets/{verify-D_QGyiLQ.js → verify-CFZQJntQ.js} +1 -1
- package/packages/ui/dist/assets/{verify-filled-DIW8QKL9.js → verify-filled-Cy6vpeJk.js} +1 -1
- package/packages/ui/dist/assets/{w3m-modal-Do9U160p.js → w3m-modal-De9EZPA2.js} +1 -1
- package/packages/ui/dist/assets/{wallet-CcARZnOx.js → wallet-BfWc3N5d.js} +1 -1
- package/packages/ui/dist/assets/{wallet-placeholder-X1coFzQa.js → wallet-placeholder-BuxnWFqL.js} +1 -1
- package/packages/ui/dist/assets/{walletconnect-Glte9ia7.js → walletconnect-CjirfANF.js} +1 -1
- package/packages/ui/dist/assets/{warning-circle-j-3V4KTo.js → warning-circle-CqS0eXEs.js} +1 -1
- package/packages/ui/dist/assets/{x-Bcc52c_T.js → x-DQeWAjll.js} +1 -1
- package/packages/ui/dist/index.html +2 -2
- package/templates/custom-mandate/README.md +31 -0
- package/templates/custom-mandate/mandates/BoundedCallPermission.sol +8 -2
- package/templates/custom-mandate/mandates/SailCalldata.sol +118 -0
- package/templates/default/AGENTS.md +51 -2
- package/packages/ui/dist/assets/cursor-BAViuJWh.js +0 -3
- package/packages/ui/dist/assets/index-DDKDa0s2.css +0 -1
|
@@ -38416,6 +38416,7 @@ function getRpcUrl(chainId) {
|
|
|
38416
38416
|
}
|
|
38417
38417
|
|
|
38418
38418
|
// src/lib/keys.ts
|
|
38419
|
+
init_esm2();
|
|
38419
38420
|
var ROLES = ["manager", "permissionSigner"];
|
|
38420
38421
|
function normalizeRole(input) {
|
|
38421
38422
|
const n = input.trim().toLowerCase().replace(/[-_\s]/g, "");
|
|
@@ -38482,6 +38483,13 @@ async function loadManagerSigner(safe) {
|
|
|
38482
38483
|
}
|
|
38483
38484
|
return loadKeyring("manager", safe);
|
|
38484
38485
|
}
|
|
38486
|
+
function managerKeystorePath(managerAddr) {
|
|
38487
|
+
if (!isAddress(managerAddr, { strict: false })) {
|
|
38488
|
+
throw new Error(`managerKeystorePath: invalid address "${managerAddr}"`);
|
|
38489
|
+
}
|
|
38490
|
+
const hex = managerAddr.toLowerCase().replace(/^0x/, "");
|
|
38491
|
+
return sailPath("keys", "managers", `${hex}.json`);
|
|
38492
|
+
}
|
|
38485
38493
|
async function loadAnySigner() {
|
|
38486
38494
|
if (keyExists("permissionSigner")) return loadKeyring("permissionSigner");
|
|
38487
38495
|
if (keyExists("manager")) return loadKeyring("manager");
|
|
@@ -39899,11 +39907,29 @@ init_esm2();
|
|
|
39899
39907
|
async function checkContractExists(pc, address) {
|
|
39900
39908
|
try {
|
|
39901
39909
|
const code = await pc.getCode({ address });
|
|
39902
|
-
|
|
39910
|
+
const hasCode = !!code && code !== "0x";
|
|
39911
|
+
return { address, hasCode, bytecode: hasCode ? code : void 0 };
|
|
39903
39912
|
} catch (err) {
|
|
39904
39913
|
return { address, hasCode: false, error: err.message.split("\n")[0] };
|
|
39905
39914
|
}
|
|
39906
39915
|
}
|
|
39916
|
+
function checkSelectorRoutes(calldata, bytecode) {
|
|
39917
|
+
if (calldata.length < 10) {
|
|
39918
|
+
return { selector: "", routes: null, reason: "calldata shorter than 4 bytes \u2014 no selector" };
|
|
39919
|
+
}
|
|
39920
|
+
const selector = calldata.slice(2, 10).toLowerCase();
|
|
39921
|
+
const body = bytecode.slice(2).toLowerCase();
|
|
39922
|
+
if (body.length < 100) {
|
|
39923
|
+
return { selector, routes: null, reason: "proxy or minimal contract \u2014 routing not determinable from bytecode" };
|
|
39924
|
+
}
|
|
39925
|
+
if (body.includes("360894a13ba1a321")) {
|
|
39926
|
+
return { selector, routes: null, reason: "EIP-1967 proxy detected \u2014 routing is in the implementation contract" };
|
|
39927
|
+
}
|
|
39928
|
+
if (body.includes("a3f0ad74e5423aeb")) {
|
|
39929
|
+
return { selector, routes: null, reason: "EIP-1967 beacon proxy detected \u2014 routing is in the beacon implementation" };
|
|
39930
|
+
}
|
|
39931
|
+
return { selector, routes: body.includes(selector) };
|
|
39932
|
+
}
|
|
39907
39933
|
|
|
39908
39934
|
// src/lib/permission-resolver.ts
|
|
39909
39935
|
var IPERMISSION_ABI = [
|
|
@@ -40775,14 +40801,48 @@ var MandateStore = class {
|
|
|
40775
40801
|
(m) => m.address.toLowerCase() === needle || m.name === addressOrName
|
|
40776
40802
|
);
|
|
40777
40803
|
}
|
|
40778
|
-
/**
|
|
40804
|
+
/**
|
|
40805
|
+
* Append a newly deployed mandate (replacing any prior record at the same address).
|
|
40806
|
+
* When another mandate with the same name already exists on the same chain, the
|
|
40807
|
+
* incoming mandate's name is suffixed with `[2]`, `[3]`, … to keep names unique.
|
|
40808
|
+
*/
|
|
40779
40809
|
add(mandate2) {
|
|
40780
40810
|
const data = this.read();
|
|
40781
40811
|
data.mandates = data.mandates.filter(
|
|
40782
40812
|
(m) => m.address.toLowerCase() !== mandate2.address.toLowerCase()
|
|
40783
40813
|
);
|
|
40814
|
+
const baseName = mandate2.name;
|
|
40815
|
+
const sameName = (m) => m.name === mandate2.name && m.chainId === mandate2.chainId;
|
|
40816
|
+
if (data.mandates.some(sameName)) {
|
|
40817
|
+
let n = 2;
|
|
40818
|
+
while (data.mandates.some((m) => m.name === `${baseName}[${n}]` && m.chainId === mandate2.chainId)) {
|
|
40819
|
+
n++;
|
|
40820
|
+
}
|
|
40821
|
+
mandate2 = { ...mandate2, name: `${baseName}[${n}]` };
|
|
40822
|
+
}
|
|
40784
40823
|
data.mandates.push(mandate2);
|
|
40785
40824
|
this.write(data);
|
|
40825
|
+
return mandate2;
|
|
40826
|
+
}
|
|
40827
|
+
/** Update mutable metadata fields on a tracked mandate (name, sourcePath, artifactPath). */
|
|
40828
|
+
update(addressOrName, patch) {
|
|
40829
|
+
const data = this.read();
|
|
40830
|
+
const needle = addressOrName.toLowerCase();
|
|
40831
|
+
const mandate2 = data.mandates.find(
|
|
40832
|
+
(m) => m.address.toLowerCase() === needle || m.name === addressOrName
|
|
40833
|
+
);
|
|
40834
|
+
if (!mandate2) throw new Error(`No tracked mandate found for: ${addressOrName}`);
|
|
40835
|
+
if (patch.name !== void 0 && patch.name !== mandate2.name) {
|
|
40836
|
+
const conflict = data.mandates.find(
|
|
40837
|
+
(m) => m.name === patch.name && m.chainId === mandate2.chainId && m.address.toLowerCase() !== mandate2.address.toLowerCase()
|
|
40838
|
+
);
|
|
40839
|
+
if (conflict) throw new Error(`Name "${patch.name}" is already used by ${conflict.address} on chain ${mandate2.chainId}`);
|
|
40840
|
+
mandate2.name = patch.name;
|
|
40841
|
+
}
|
|
40842
|
+
if (patch.sourcePath !== void 0) mandate2.sourcePath = patch.sourcePath;
|
|
40843
|
+
if (patch.artifactPath !== void 0) mandate2.artifactPath = patch.artifactPath;
|
|
40844
|
+
this.write(data);
|
|
40845
|
+
return mandate2;
|
|
40786
40846
|
}
|
|
40787
40847
|
/** Record that a tracked mandate was attached to an SMA. */
|
|
40788
40848
|
recordAttachment(address, attachment) {
|
|
@@ -41290,6 +41350,7 @@ async function persistAccount(publicClient, account2) {
|
|
|
41290
41350
|
owner: checksum4(account2.owner),
|
|
41291
41351
|
permissionSigner: checksum4(account2.permissionSigner),
|
|
41292
41352
|
manager: checksum4(account2.manager),
|
|
41353
|
+
managers: [checksum4(account2.manager)],
|
|
41293
41354
|
chainId: account2.chainId,
|
|
41294
41355
|
createdAtBlock,
|
|
41295
41356
|
...account2.saltNonce != null ? { saltNonce: account2.saltNonce.toString() } : {}
|
|
@@ -41396,7 +41457,18 @@ async function runDeploy(project, channel, options) {
|
|
|
41396
41457
|
);
|
|
41397
41458
|
}
|
|
41398
41459
|
const { abi: abi2, bytecode, contractName, artifactPath } = resolveArtifact(options);
|
|
41399
|
-
|
|
41460
|
+
let argsJson;
|
|
41461
|
+
if (options.argsFile) {
|
|
41462
|
+
const argsFilePath = (0, import_node_path9.resolve)(options.argsFile);
|
|
41463
|
+
try {
|
|
41464
|
+
argsJson = (0, import_node_fs10.readFileSync)(argsFilePath, "utf8").trim();
|
|
41465
|
+
} catch {
|
|
41466
|
+
throw new Error(`Cannot read --args-file: ${argsFilePath}`);
|
|
41467
|
+
}
|
|
41468
|
+
} else {
|
|
41469
|
+
argsJson = options.args;
|
|
41470
|
+
}
|
|
41471
|
+
const args = coerceConstructorArgs(abi2, argsJson);
|
|
41400
41472
|
const deployData = encodeDeployData({ abi: abi2, bytecode, args });
|
|
41401
41473
|
const chainId = project.chainId;
|
|
41402
41474
|
const publicClient = publicClientFor(project);
|
|
@@ -41419,7 +41491,10 @@ async function runDeploy(project, channel, options) {
|
|
|
41419
41491
|
data: deployData,
|
|
41420
41492
|
details: [
|
|
41421
41493
|
{ label: "Contract", value: contractName },
|
|
41422
|
-
{
|
|
41494
|
+
{
|
|
41495
|
+
label: options.argsFile ? "Constructor args (from file)" : "Constructor args",
|
|
41496
|
+
value: argsJson ? argsJson : "(none)"
|
|
41497
|
+
}
|
|
41423
41498
|
]
|
|
41424
41499
|
});
|
|
41425
41500
|
if (response.status === "rejected") {
|
|
@@ -41445,13 +41520,13 @@ async function runDeploy(project, channel, options) {
|
|
|
41445
41520
|
deployedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
41446
41521
|
};
|
|
41447
41522
|
const store = new MandateStore();
|
|
41448
|
-
store.add(record);
|
|
41449
|
-
say(() => console.log("Tracked in .sail/state/mandates.json"));
|
|
41523
|
+
const stored = store.add(record);
|
|
41524
|
+
say(() => console.log("Tracked in .sail/state/mandates.json" + (stored.name !== record.name ? ` as "${stored.name}"` : "")));
|
|
41450
41525
|
appendActivity({
|
|
41451
41526
|
ts: nowIso(),
|
|
41452
41527
|
actor: "owner",
|
|
41453
41528
|
type: "mandate_deployed",
|
|
41454
|
-
name:
|
|
41529
|
+
name: stored.name,
|
|
41455
41530
|
address: deployed,
|
|
41456
41531
|
txHash: response.txHash,
|
|
41457
41532
|
chainId
|
|
@@ -41465,7 +41540,7 @@ async function runDeploy(project, channel, options) {
|
|
|
41465
41540
|
publicClient,
|
|
41466
41541
|
sma,
|
|
41467
41542
|
deployed,
|
|
41468
|
-
|
|
41543
|
+
stored.name,
|
|
41469
41544
|
json
|
|
41470
41545
|
);
|
|
41471
41546
|
store.recordAttachment(deployed, { sma, txHash: attachTxHash });
|
|
@@ -41480,7 +41555,7 @@ Register it later with: sailor mandate attach --address ${deployed} --sma <SMA>`
|
|
|
41480
41555
|
emit(json, () => {
|
|
41481
41556
|
}, {
|
|
41482
41557
|
status: "ok",
|
|
41483
|
-
mandate: { name:
|
|
41558
|
+
mandate: { name: stored.name, address: deployed, txHash: response.txHash, chainId },
|
|
41484
41559
|
attached: options.attach ? { sma: getAddress(options.sma), txHash: attachTxHash } : null
|
|
41485
41560
|
});
|
|
41486
41561
|
}
|
|
@@ -41742,7 +41817,7 @@ Connect the owner wallet (mandate signer) in the browser \u2014 the agent wallet
|
|
|
41742
41817
|
}
|
|
41743
41818
|
say(() => console.log("\u2713", `Deployed + registered ${spec.label} at ${clone}`));
|
|
41744
41819
|
const store = new MandateStore();
|
|
41745
|
-
store.add({
|
|
41820
|
+
const storedClone = store.add({
|
|
41746
41821
|
name: label,
|
|
41747
41822
|
address: clone,
|
|
41748
41823
|
txHash,
|
|
@@ -41755,7 +41830,7 @@ Connect the owner wallet (mandate signer) in the browser \u2014 the agent wallet
|
|
|
41755
41830
|
actor: "agent",
|
|
41756
41831
|
type: "permission_registered",
|
|
41757
41832
|
permission: clone,
|
|
41758
|
-
name:
|
|
41833
|
+
name: storedClone.name,
|
|
41759
41834
|
sma,
|
|
41760
41835
|
txHash,
|
|
41761
41836
|
chainId: project.chainId
|
|
@@ -42076,6 +42151,21 @@ function mandateContractsList() {
|
|
|
42076
42151
|
}
|
|
42077
42152
|
}
|
|
42078
42153
|
}
|
|
42154
|
+
function mandateUpdate(options) {
|
|
42155
|
+
const { address, name, sourcePath, artifactPath, json } = options;
|
|
42156
|
+
if (!name && !sourcePath && !artifactPath) {
|
|
42157
|
+
throw new Error("Provide at least one of --name, --source-path, or --artifact-path");
|
|
42158
|
+
}
|
|
42159
|
+
const store = new MandateStore();
|
|
42160
|
+
const updated = store.update(address, { name, sourcePath, artifactPath });
|
|
42161
|
+
emit(!!json, () => {
|
|
42162
|
+
const changes = [];
|
|
42163
|
+
if (name) changes.push(`name \u2192 ${updated.name}`);
|
|
42164
|
+
if (sourcePath) changes.push(`sourcePath \u2192 ${updated.sourcePath}`);
|
|
42165
|
+
if (artifactPath) changes.push(`artifactPath \u2192 ${updated.artifactPath}`);
|
|
42166
|
+
console.log(`Updated ${updated.address}: ${changes.join(", ")}`);
|
|
42167
|
+
}, { status: "ok", mandate: updated });
|
|
42168
|
+
}
|
|
42079
42169
|
function resolveArtifact(options) {
|
|
42080
42170
|
let artifactPath = options.artifact;
|
|
42081
42171
|
let contractName = options.contract ?? options.name ?? "";
|
|
@@ -42085,7 +42175,7 @@ function resolveArtifact(options) {
|
|
|
42085
42175
|
}
|
|
42086
42176
|
const resolved = (0, import_node_path9.resolve)(artifactPath);
|
|
42087
42177
|
const projectRoot = (0, import_node_path9.resolve)(process.cwd());
|
|
42088
|
-
if (!resolved.startsWith(projectRoot +
|
|
42178
|
+
if (!resolved.startsWith(projectRoot + import_node_path9.sep) && resolved !== projectRoot) {
|
|
42089
42179
|
throw new Error(
|
|
42090
42180
|
`Artifact path must be inside the project directory.
|
|
42091
42181
|
Resolved: ${resolved}`
|
|
@@ -42530,6 +42620,7 @@ async function mandateSimulate(options) {
|
|
|
42530
42620
|
checkContractExists(pc, c.target)
|
|
42531
42621
|
]);
|
|
42532
42622
|
const result = probe.accepted ? "pass" : "fail";
|
|
42623
|
+
const selectorCheck = codeCheck.hasCode && codeCheck.bytecode ? checkSelectorRoutes(c.data, codeCheck.bytecode) : { selector: "", routes: null, reason: codeCheck.hasCode ? "bytecode unavailable" : void 0 };
|
|
42533
42624
|
return {
|
|
42534
42625
|
index: i,
|
|
42535
42626
|
label: c.label,
|
|
@@ -42541,7 +42632,10 @@ async function mandateSimulate(options) {
|
|
|
42541
42632
|
expect: c.expect ?? null,
|
|
42542
42633
|
match: c.expect ? c.expect === result : null,
|
|
42543
42634
|
targetHasCode: codeCheck.hasCode,
|
|
42544
|
-
targetCheckError: codeCheck.error
|
|
42635
|
+
targetCheckError: codeCheck.error,
|
|
42636
|
+
selectorRoutes: selectorCheck.routes,
|
|
42637
|
+
selector: selectorCheck.selector,
|
|
42638
|
+
selectorRoutesReason: selectorCheck.reason
|
|
42545
42639
|
};
|
|
42546
42640
|
})
|
|
42547
42641
|
);
|
|
@@ -42572,7 +42666,10 @@ async function mandateSimulate(options) {
|
|
|
42572
42666
|
expect: r.expect,
|
|
42573
42667
|
match: r.match,
|
|
42574
42668
|
targetHasCode: r.targetHasCode,
|
|
42575
|
-
targetCheckError: r.targetCheckError
|
|
42669
|
+
targetCheckError: r.targetCheckError,
|
|
42670
|
+
selector: r.selector,
|
|
42671
|
+
selectorRoutes: r.selectorRoutes,
|
|
42672
|
+
selectorRoutesReason: r.selectorRoutesReason
|
|
42576
42673
|
})),
|
|
42577
42674
|
mismatches: mismatches.length,
|
|
42578
42675
|
noCodeTargets: noCodeTargets.map((r) => r.target),
|
|
@@ -42598,7 +42695,9 @@ async function mandateSimulate(options) {
|
|
|
42598
42695
|
const expectStr = r.expect === null ? "" : r.match ? ` expected ${r.expect} \u2713 MATCH` : ` expected ${r.expect} \u2717 MISMATCH`;
|
|
42599
42696
|
console.log(`[${r.index + 1}] ${verdict} ${r.label}${expectStr}`);
|
|
42600
42697
|
const codeNote = r.targetCheckError ? `\u26A0 could not verify contract code (${r.targetCheckError})` : r.targetHasCode ? "\u2713 contract present" : `\u26A0 NO contract code on chain ${chainId} \u2014 this call would fail on-chain regardless of the permission`;
|
|
42698
|
+
const selectorNote = r.selectorRoutes === true ? `\u2713 selector 0x${r.selector} routes` : r.selectorRoutes === false ? `\u26A0 selector 0x${r.selector} NOT found in bytecode \u2014 call would likely revert with unknown selector` : r.selectorRoutesReason ? `~ selector check skipped (${r.selectorRoutesReason})` : null;
|
|
42601
42699
|
console.log(` target ${r.target} ${codeNote}`);
|
|
42700
|
+
if (selectorNote) console.log(` ${selectorNote}`);
|
|
42602
42701
|
if (r.reverted && r.revertReason) {
|
|
42603
42702
|
console.log(` evaluate() reverted: ${r.revertReason}`);
|
|
42604
42703
|
}
|
|
@@ -42661,6 +42760,24 @@ async function runRotateSigner(project, channel, options) {
|
|
|
42661
42760
|
const account2 = resolveAccount(options);
|
|
42662
42761
|
const smaAddress = account2.safe;
|
|
42663
42762
|
const owner2 = account2.owner;
|
|
42763
|
+
if (options.list) {
|
|
42764
|
+
const stored = readJsonFile(sailPath("account.json"));
|
|
42765
|
+
const known = stored?.managers ?? (stored?.manager ? [stored.manager] : []);
|
|
42766
|
+
const active = stored?.manager ?? "";
|
|
42767
|
+
return {
|
|
42768
|
+
sma: smaAddress,
|
|
42769
|
+
oldManager: null,
|
|
42770
|
+
newManager: active,
|
|
42771
|
+
rotated: false,
|
|
42772
|
+
reattached: [],
|
|
42773
|
+
reattachDeferred: false,
|
|
42774
|
+
managers: known.map((m) => ({
|
|
42775
|
+
address: m,
|
|
42776
|
+
active: m.toLowerCase() === active.toLowerCase(),
|
|
42777
|
+
keystoreStored: readJsonFile(managerKeystorePath(m)) !== null
|
|
42778
|
+
}))
|
|
42779
|
+
};
|
|
42780
|
+
}
|
|
42664
42781
|
const publicClient = createPublicClient({
|
|
42665
42782
|
chain: getChainById(project.chainId),
|
|
42666
42783
|
transport: http(getRpcUrl(project.chainId))
|
|
@@ -42793,6 +42910,7 @@ SMA: ${smaAddress}`);
|
|
|
42793
42910
|
chainId: project.chainId
|
|
42794
42911
|
});
|
|
42795
42912
|
persistManager(smaAddress, newManager);
|
|
42913
|
+
if (options.to) promoteManagerKeystore(newManager, say);
|
|
42796
42914
|
if (options.skipReattach || currentPermissions.length === 0) {
|
|
42797
42915
|
return {
|
|
42798
42916
|
sma: smaAddress,
|
|
@@ -42956,13 +43074,23 @@ async function resolveNewManager(options, oldManager, json, say) {
|
|
|
42956
43074
|
throw new Error(`Invalid --to address: ${options.to}`);
|
|
42957
43075
|
}
|
|
42958
43076
|
const to = getAddress(options.to);
|
|
42959
|
-
|
|
42960
|
-
(
|
|
42961
|
-
|
|
42962
|
-
|
|
43077
|
+
if (readJsonFile(managerKeystorePath(to)) !== null) {
|
|
43078
|
+
say(
|
|
43079
|
+
() => console.log(
|
|
43080
|
+
`
|
|
43081
|
+
Rotating to ${to}. Its stored keystore will become the active one
|
|
43082
|
+
(.sail/keys/manager.json) once the rotation confirms.`
|
|
43083
|
+
)
|
|
43084
|
+
);
|
|
43085
|
+
} else {
|
|
43086
|
+
say(
|
|
43087
|
+
() => console.log(
|
|
43088
|
+
`
|
|
43089
|
+
Rotating to existing address ${to}. No local keystore found for this address \u2014
|
|
42963
43090
|
ensure the agent that signs dispatches holds this key.`
|
|
42964
|
-
|
|
42965
|
-
|
|
43091
|
+
)
|
|
43092
|
+
);
|
|
43093
|
+
}
|
|
42966
43094
|
return to;
|
|
42967
43095
|
}
|
|
42968
43096
|
if (json) {
|
|
@@ -42982,6 +43110,9 @@ ensure the agent that signs dispatches holds this key.`
|
|
|
42982
43110
|
const keyring = LocalKeyring.generate();
|
|
42983
43111
|
const keystore = await keyring.exportKeystore(password);
|
|
42984
43112
|
writeJsonFile(target, keystore);
|
|
43113
|
+
const perManagerPath = managerKeystorePath(keyring.address);
|
|
43114
|
+
(0, import_node_fs13.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
|
|
43115
|
+
writeJsonFile(perManagerPath, keystore);
|
|
42985
43116
|
say(
|
|
42986
43117
|
() => console.log(
|
|
42987
43118
|
` New agent wallet: ${checksum4(keyring.address)} (keystore at .sail/keys/manager.json)`
|
|
@@ -42989,10 +43120,28 @@ ensure the agent that signs dispatches holds this key.`
|
|
|
42989
43120
|
);
|
|
42990
43121
|
return keyring.address;
|
|
42991
43122
|
}
|
|
43123
|
+
function promoteManagerKeystore(newManager, say) {
|
|
43124
|
+
const stored = readJsonFile(managerKeystorePath(newManager));
|
|
43125
|
+
if (!stored) return;
|
|
43126
|
+
(0, import_node_fs13.mkdirSync)(sailPath("keys", "managers"), { recursive: true });
|
|
43127
|
+
const activeTarget = keyPath("manager");
|
|
43128
|
+
const displaced = readJsonFile(activeTarget);
|
|
43129
|
+
if (displaced?.address) {
|
|
43130
|
+
const snapshotPath = managerKeystorePath(displaced.address);
|
|
43131
|
+
if (readJsonFile(snapshotPath) === null) writeJsonFile(snapshotPath, displaced);
|
|
43132
|
+
} else if (displaced) {
|
|
43133
|
+
writeJsonFile(`${activeTarget}.${Date.now()}.bak`, displaced);
|
|
43134
|
+
}
|
|
43135
|
+
writeJsonFile(activeTarget, stored);
|
|
43136
|
+
say(
|
|
43137
|
+
() => console.log(` Agent wallet keystore for ${newManager} is now active (.sail/keys/manager.json).`)
|
|
43138
|
+
);
|
|
43139
|
+
}
|
|
42992
43140
|
function persistManager(safe, manager) {
|
|
42993
43141
|
const account2 = readJsonFile(sailPath("account.json"));
|
|
42994
43142
|
if (account2 && account2.safe.toLowerCase() === safe.toLowerCase()) {
|
|
42995
|
-
|
|
43143
|
+
const managers = addToManagerList(account2.managers, account2.manager, manager);
|
|
43144
|
+
writeJsonFile(sailPath("account.json"), { ...account2, manager: checksum4(manager), managers });
|
|
42996
43145
|
}
|
|
42997
43146
|
const listPath = sailPath("state", "accounts.json");
|
|
42998
43147
|
const list = readJsonFile(
|
|
@@ -43001,11 +43150,26 @@ function persistManager(safe, manager) {
|
|
|
43001
43150
|
if (Array.isArray(list)) {
|
|
43002
43151
|
const idx = list.findIndex((a) => a.safe.toLowerCase() === safe.toLowerCase());
|
|
43003
43152
|
if (idx !== -1) {
|
|
43004
|
-
|
|
43153
|
+
const entry = list[idx];
|
|
43154
|
+
const managers = addToManagerList(entry.managers, entry.manager, manager);
|
|
43155
|
+
list[idx] = { ...entry, manager: checksum4(manager), managers };
|
|
43005
43156
|
writeJsonFile(listPath, list);
|
|
43006
43157
|
}
|
|
43007
43158
|
}
|
|
43008
43159
|
}
|
|
43160
|
+
function addToManagerList(existing, current, next) {
|
|
43161
|
+
const all = [...existing ?? [current], checksum4(next)];
|
|
43162
|
+
const seen = /* @__PURE__ */ new Set();
|
|
43163
|
+
const deduped = [];
|
|
43164
|
+
for (const a of all) {
|
|
43165
|
+
const lower = a.toLowerCase();
|
|
43166
|
+
if (!seen.has(lower)) {
|
|
43167
|
+
seen.add(lower);
|
|
43168
|
+
deduped.push(checksum4(a));
|
|
43169
|
+
}
|
|
43170
|
+
}
|
|
43171
|
+
return deduped;
|
|
43172
|
+
}
|
|
43009
43173
|
function writePending(pending) {
|
|
43010
43174
|
writeJsonFile(sailPath(...PENDING_REATTACH_FILE), pending);
|
|
43011
43175
|
}
|
|
@@ -43016,6 +43180,19 @@ function clearPending() {
|
|
|
43016
43180
|
}
|
|
43017
43181
|
}
|
|
43018
43182
|
function printSummary2(r) {
|
|
43183
|
+
if (r.managers) {
|
|
43184
|
+
console.log("\nKnown agent wallets for this SMA:");
|
|
43185
|
+
if (r.managers.length === 0) {
|
|
43186
|
+
console.log(" (none recorded)");
|
|
43187
|
+
} else {
|
|
43188
|
+
for (const m of r.managers) {
|
|
43189
|
+
const marker = m.active ? "* " : " ";
|
|
43190
|
+
console.log(`${marker}${m.address}${m.keystoreStored ? " (keystore stored)" : ""}`);
|
|
43191
|
+
}
|
|
43192
|
+
console.log("\n* = active");
|
|
43193
|
+
}
|
|
43194
|
+
return;
|
|
43195
|
+
}
|
|
43019
43196
|
console.log(`
|
|
43020
43197
|
${"\u2500".repeat(56)}`);
|
|
43021
43198
|
if (r.rotated) {
|
|
@@ -43138,13 +43315,30 @@ function isProcessAlive(pid) {
|
|
|
43138
43315
|
// src/commands/run.ts
|
|
43139
43316
|
var DEFAULT_INTERVAL_SEC = 60;
|
|
43140
43317
|
var sleep2 = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
43141
|
-
var
|
|
43318
|
+
var ERC20_READ_ABI = [
|
|
43142
43319
|
{
|
|
43143
43320
|
type: "function",
|
|
43144
43321
|
name: "balanceOf",
|
|
43145
43322
|
stateMutability: "view",
|
|
43146
43323
|
inputs: [{ name: "account", type: "address" }],
|
|
43147
43324
|
outputs: [{ name: "", type: "uint256" }]
|
|
43325
|
+
},
|
|
43326
|
+
{
|
|
43327
|
+
type: "function",
|
|
43328
|
+
name: "allowance",
|
|
43329
|
+
stateMutability: "view",
|
|
43330
|
+
inputs: [
|
|
43331
|
+
{ name: "owner", type: "address" },
|
|
43332
|
+
{ name: "spender", type: "address" }
|
|
43333
|
+
],
|
|
43334
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
43335
|
+
},
|
|
43336
|
+
{
|
|
43337
|
+
type: "function",
|
|
43338
|
+
name: "decimals",
|
|
43339
|
+
stateMutability: "view",
|
|
43340
|
+
inputs: [],
|
|
43341
|
+
outputs: [{ name: "", type: "uint8" }]
|
|
43148
43342
|
}
|
|
43149
43343
|
];
|
|
43150
43344
|
function loadAgentData(filePath) {
|
|
@@ -43164,7 +43358,8 @@ async function loadAgent() {
|
|
|
43164
43358
|
let mod2;
|
|
43165
43359
|
if (abs.endsWith(".ts")) {
|
|
43166
43360
|
const { tsImport } = await import("tsx/esm/api");
|
|
43167
|
-
|
|
43361
|
+
const absUrl = (0, import_node_url.pathToFileURL)(abs).href;
|
|
43362
|
+
mod2 = await tsImport(absUrl, absUrl);
|
|
43168
43363
|
} else {
|
|
43169
43364
|
mod2 = await import((0, import_node_url.pathToFileURL)(abs).href);
|
|
43170
43365
|
}
|
|
@@ -43291,11 +43486,30 @@ Configure the chain in @sail/chains or set KERNEL_ADDRESS in .sail/.env.local.`
|
|
|
43291
43486
|
}
|
|
43292
43487
|
return publicClient.readContract({
|
|
43293
43488
|
address: token,
|
|
43294
|
-
abi:
|
|
43489
|
+
abi: ERC20_READ_ABI,
|
|
43295
43490
|
functionName: "balanceOf",
|
|
43296
43491
|
args: [accountAddr]
|
|
43297
43492
|
});
|
|
43298
43493
|
};
|
|
43494
|
+
const readAllowance = (token, owner2, spender) => publicClient.readContract({
|
|
43495
|
+
address: token,
|
|
43496
|
+
abi: ERC20_READ_ABI,
|
|
43497
|
+
functionName: "allowance",
|
|
43498
|
+
args: [owner2, spender]
|
|
43499
|
+
});
|
|
43500
|
+
const decimalsCache = /* @__PURE__ */ new Map();
|
|
43501
|
+
const readDecimals = async (token) => {
|
|
43502
|
+
const key = getAddress(token);
|
|
43503
|
+
const cached = decimalsCache.get(key);
|
|
43504
|
+
if (cached !== void 0) return cached;
|
|
43505
|
+
const d = await publicClient.readContract({
|
|
43506
|
+
address: token,
|
|
43507
|
+
abi: ERC20_READ_ABI,
|
|
43508
|
+
functionName: "decimals"
|
|
43509
|
+
});
|
|
43510
|
+
decimalsCache.set(key, d);
|
|
43511
|
+
return d;
|
|
43512
|
+
};
|
|
43299
43513
|
async function runTick() {
|
|
43300
43514
|
appendActivity({ ts: nowIso(), actor: "agent", type: "tick_start" });
|
|
43301
43515
|
let blockInfo = { number: 0n, timestamp: 0n };
|
|
@@ -43325,10 +43539,15 @@ Configure the chain in @sail/chains or set KERNEL_ADDRESS in .sail/.env.local.`
|
|
|
43325
43539
|
dispatch: execClient.dispatch,
|
|
43326
43540
|
strategy: execClient.strategy
|
|
43327
43541
|
}),
|
|
43542
|
+
publicClient,
|
|
43328
43543
|
manager: agentManager,
|
|
43329
43544
|
log,
|
|
43330
43545
|
data: agentData,
|
|
43331
|
-
read: {
|
|
43546
|
+
read: {
|
|
43547
|
+
balance: readBalance,
|
|
43548
|
+
allowance: readAllowance,
|
|
43549
|
+
decimals: readDecimals
|
|
43550
|
+
}
|
|
43332
43551
|
};
|
|
43333
43552
|
let dispatches;
|
|
43334
43553
|
try {
|
|
@@ -43949,11 +44168,11 @@ account.command("predict").description(
|
|
|
43949
44168
|
"Agent (manager) wallet \u2014 mixed into the kernel salt (defaults to .sail/account.json)"
|
|
43950
44169
|
).option("--salt <n>", "CREATE2 salt nonce (default: 0)").option("--chain <id>", "Show prediction for one chain only").option("--json", "Emit machine-readable JSON").action(actionWith(accountPredict));
|
|
43951
44170
|
account.command("deploy-chain").description("Deploy the same SMA address on an additional chain using the same owner, manager, and salt").requiredOption("--chain <id>", "Target EVM chain ID (e.g. 8453, 42161, 130, 1)").option("--salt <n>", "CREATE2 salt (defaults to saltNonce stored in .sail/account.json)").option("--json", "Emit machine-readable JSON").action(actionWith(accountDeployChain));
|
|
43952
|
-
account.command("rotate-signer").description("Rotate the SMA's delegated signer (agent wallet) and re-approve its mandates").option("--sma <address>", "SMA to rotate (defaults to the active account)").option("--to <address>", "Rotate to an existing agent-wallet address instead of generating one").option("--generate", "Generate a fresh local agent wallet (default when --to is omitted)").option("--skip-reattach", "Do not re-approve the previously-attached mandates").option("--reattach-only", "Skip rotation; only re-approve mandates (resume after funding)").option("--json", "Machine-readable output").action(actionWith(rotateSigner));
|
|
44171
|
+
account.command("rotate-signer").description("Rotate the SMA's delegated signer (agent wallet) and re-approve its mandates").option("--sma <address>", "SMA to rotate (defaults to the active account)").option("--to <address>", "Rotate to an existing agent-wallet address instead of generating one").option("--generate", "Generate a fresh local agent wallet (default when --to is omitted)").option("--skip-reattach", "Do not re-approve the previously-attached mandates").option("--reattach-only", "Skip rotation; only re-approve mandates (resume after funding)").option("--list", "List known agent wallets for this SMA without rotating").option("--json", "Machine-readable output").action(actionWith(rotateSigner));
|
|
43953
44172
|
var mandate = program2.command("mandate").description("Manage mandates");
|
|
43954
44173
|
mandate.command("prepare").description("Prepare a mandate draft for review and signing in the UI (MetaMask)").action(action(mandatePrepare));
|
|
43955
44174
|
mandate.command("sign").description("Review and confirm the permissions authorized for your SMA").option("--yes", "Skip the confirmation prompt (for non-interactive / CI use)").action(actionWith(mandateSign));
|
|
43956
|
-
mandate.command("deploy").description("Deploy a Foundry-compiled permission contract via the browser signing UI").option("--artifact <path>", "Path to the Foundry artifact JSON (out/<Name>.sol/<Name>.json)").option("--contract <name>", "Contract name; resolves to <out>/<name>.sol/<name>.json").option("--out <dir>", "Foundry output directory", "out").option("--name <label>", "Label to track this permission under (defaults to contract name)").option("--args <json>", `Constructor args as
|
|
44175
|
+
mandate.command("deploy").description("Deploy a Foundry-compiled permission contract via the browser signing UI").option("--artifact <path>", "Path to the Foundry artifact JSON (out/<Name>.sol/<Name>.json)").option("--contract <name>", "Contract name; resolves to <out>/<name>.sol/<name>.json").option("--out <dir>", "Foundry output directory", "out").option("--name <label>", "Label to track this permission under (defaults to contract name)").option("--args <json>", `Constructor args as JSON array. Bash: '["0x..","1"]'. PowerShell: '[\\"0x..\\",\\"1\\"]'. Use --args-file to avoid quoting.`).option("--args-file <path>", "Path to a JSON file containing constructor args array (recommended on PowerShell)").option("--build", "Run `forge build` before deploying").option("--attach", "After deploy, register the permission on --sma").option("--sma <address>", "SMA to register on (required with --attach)").option("--json", "Emit machine-readable JSON").action(actionWith(mandateDeploy));
|
|
43957
44176
|
mandate.command("attach").description("Register an already-deployed permission on an SMA (EIP-712 RegisterPermission)").requiredOption("--address <mandateOrName>", "Permission address, or a name tracked locally").requiredOption("--sma <address>", "SMA to register the permission on").option("--label <label>", "Human-readable label shown in the signing UI").option("--json", "Emit machine-readable JSON").action(actionWith(mandateAttach));
|
|
43958
44177
|
mandate.command("deploy-clone").description("Deploy + register a standalone clone permission (e.g. boundedApprove) via the signing UI").requiredOption("--template <key>", "Standalone clone template key (e.g. boundedApprove)").requiredOption("--sma <address>", "SMA to deploy the clone for and register it on").option("--tokens <csv>", "Comma-separated allowed token addresses").option("--spenders <csv>", "Comma-separated allowed spender addresses").option("--max <amount>", "Max amount per tx in base units (default: uint256 max)").option("--label <label>", "Human-readable label to track this permission under").option("--json", "Emit machine-readable JSON").action(actionWith(mandateDeployClone));
|
|
43959
44178
|
mandate.command("revoke").description("Revoke permission(s) from an SMA (EIP-712 RevokePermissions, owner-authorized)").option("--address <permissionOrName>", "Permission address, or a name tracked locally").requiredOption("--sma <address>", "Safe (SMA) to revoke the permission(s) from").option("--all", "Revoke every permission currently registered on the SMA").option("--json", "Output JSON").action(actionWith(mandateRevoke));
|
|
@@ -43961,6 +44180,7 @@ mandate.command("templates").description("Show how to author your own permission
|
|
|
43961
44180
|
mandate.command("simulate").description(
|
|
43962
44181
|
"Probe a permission against sample calls off-chain (eth_call, NO gas) \u2014 prove it accepts the calls you want and rejects the ones you don't, before authorizing on-chain"
|
|
43963
44182
|
).requiredOption("--address <permissionOrName>", "Permission to probe (address or tracked name)").option("--sma <address>", "SMA to probe as (ctx.account; defaults to .sail/account.json)").option("--target <address>", "Inline single call: target contract address").option("--calldata <hex>", "Inline single call: 0x-prefixed calldata").option("--value <wei>", "Inline single call: ETH value in wei (default 0)").option("--expect <pass|fail>", "Inline single call: expected outcome (sets non-zero exit on mismatch)").option("--label <text>", "Inline single call: human-readable label").option("--calls <file>", "Batch: JSON array of { target, calldata, value?, expect?, label? }").option("--json", "Emit machine-readable JSON").action(actionWith(mandateSimulate));
|
|
44183
|
+
mandate.command("update").description("Update metadata for a tracked permission contract (rename, source path, artifact path)").requiredOption("--address <mandateOrName>", "Permission address or tracked name to update").option("--name <label>", "New tracking label (must be unique within the same chain)").option("--source-path <path>", "Update the relative path to the Solidity source file").option("--artifact-path <path>", "Update the relative path to the Foundry artifact JSON").option("--json", "Emit machine-readable JSON").action(actionWith(mandateUpdate));
|
|
43964
44184
|
mandate.command("list").description("List permission contracts deployed from this project").action(action(async () => mandateContractsList()));
|
|
43965
44185
|
program2.command("onboard").description("Set up an SMA, register a permission, confirm the agent is operational").option("--sma <address>", "Use a specific SMA address instead of prompting").option("--new-sma", "Create a new SMA via SailKernel").option("--salt <n>", "CREATE2 salt for deterministic Safe address (default: 0; use 0 for first SMA, increment for subsequent)").option("--template <kindOrAddress>", "Register this permission contract (kind, label, or address)").option("--skip-mandate", "Skip the permission registration step").option("--json", "Emit machine-readable JSON (implies non-interactive)").action(actionWith(onboard));
|
|
43966
44186
|
var station = program2.command("station").description("Manage the persistent signing station (browser signing daemon)");
|
|
@@ -49593,6 +49593,17 @@ function signerEntry(role, address, balanceByAddr) {
|
|
|
49593
49593
|
status: balanceStatus(wei)
|
|
49594
49594
|
};
|
|
49595
49595
|
}
|
|
49596
|
+
function addManagerToList(existing, current, next) {
|
|
49597
|
+
const base = existing ?? (current ? [getAddress(current)] : []);
|
|
49598
|
+
const all = [...base, getAddress(next)];
|
|
49599
|
+
const seen = /* @__PURE__ */ new Set();
|
|
49600
|
+
return all.filter((a) => {
|
|
49601
|
+
const l = a.toLowerCase();
|
|
49602
|
+
if (seen.has(l)) return false;
|
|
49603
|
+
seen.add(l);
|
|
49604
|
+
return true;
|
|
49605
|
+
});
|
|
49606
|
+
}
|
|
49596
49607
|
var OVERVIEW_TTL_MS = 1e4;
|
|
49597
49608
|
function startServer(sailDir, { port = PORT } = {}) {
|
|
49598
49609
|
const app = (0, import_express.default)();
|
|
@@ -49883,7 +49894,8 @@ function startServer(sailDir, { port = PORT } = {}) {
|
|
|
49883
49894
|
try {
|
|
49884
49895
|
const account = JSON.parse(import_node_fs2.default.readFileSync(at("account.json"), "utf-8"));
|
|
49885
49896
|
if (account?.safe?.toLowerCase() === safe.toLowerCase()) {
|
|
49886
|
-
|
|
49897
|
+
const managers = addManagerToList(account.managers, account.manager, manager);
|
|
49898
|
+
import_node_fs2.default.writeFileSync(at("account.json"), `${JSON.stringify({ ...account, manager, managers }, null, 2)}
|
|
49887
49899
|
`);
|
|
49888
49900
|
}
|
|
49889
49901
|
} catch {
|
|
@@ -49893,7 +49905,9 @@ function startServer(sailDir, { port = PORT } = {}) {
|
|
|
49893
49905
|
const list = JSON.parse(import_node_fs2.default.readFileSync(listPath, "utf-8"));
|
|
49894
49906
|
const idx = list.findIndex((a) => a.safe?.toLowerCase() === safe.toLowerCase());
|
|
49895
49907
|
if (idx !== -1) {
|
|
49896
|
-
|
|
49908
|
+
const entry = list[idx];
|
|
49909
|
+
const managers = addManagerToList(entry.managers, entry.manager, manager);
|
|
49910
|
+
list[idx] = { ...entry, manager, managers };
|
|
49897
49911
|
import_node_fs2.default.writeFileSync(listPath, `${JSON.stringify(list, null, 2)}
|
|
49898
49912
|
`);
|
|
49899
49913
|
}
|
|
@@ -49915,7 +49929,20 @@ function startServer(sailDir, { port = PORT } = {}) {
|
|
|
49915
49929
|
res.status(500).json({ error: String(err) });
|
|
49916
49930
|
}
|
|
49917
49931
|
});
|
|
49918
|
-
const isManagerKeyFile = (file) => file === "manager.json" || file.startsWith("manager-") && file.endsWith(".json");
|
|
49932
|
+
const isManagerKeyFile = (file) => file === "manager.json" || file.startsWith("manager-") && file.endsWith(".json") || file.startsWith("managers/") && file.endsWith(".json");
|
|
49933
|
+
const listManagerKeyFiles = () => {
|
|
49934
|
+
let files = [];
|
|
49935
|
+
try {
|
|
49936
|
+
files = import_node_fs2.default.readdirSync(at("keys"));
|
|
49937
|
+
} catch {
|
|
49938
|
+
files = [];
|
|
49939
|
+
}
|
|
49940
|
+
try {
|
|
49941
|
+
files.push(...import_node_fs2.default.readdirSync(at("keys/managers")).map((f) => `managers/${f}`));
|
|
49942
|
+
} catch {
|
|
49943
|
+
}
|
|
49944
|
+
return files;
|
|
49945
|
+
};
|
|
49919
49946
|
const keystoreAddress = (file) => {
|
|
49920
49947
|
try {
|
|
49921
49948
|
const ks = JSON.parse(import_node_fs2.default.readFileSync(at(`keys/${file}`), "utf-8"));
|
|
@@ -49927,12 +49954,7 @@ function startServer(sailDir, { port = PORT } = {}) {
|
|
|
49927
49954
|
};
|
|
49928
49955
|
app.get("/api/signers", (_req, res) => {
|
|
49929
49956
|
try {
|
|
49930
|
-
|
|
49931
|
-
try {
|
|
49932
|
-
files = import_node_fs2.default.readdirSync(at("keys"));
|
|
49933
|
-
} catch {
|
|
49934
|
-
files = [];
|
|
49935
|
-
}
|
|
49957
|
+
const files = listManagerKeyFiles();
|
|
49936
49958
|
const activeSafe = readActiveSafe();
|
|
49937
49959
|
let activeManager = null;
|
|
49938
49960
|
try {
|
|
@@ -49972,12 +49994,7 @@ function startServer(sailDir, { port = PORT } = {}) {
|
|
|
49972
49994
|
return;
|
|
49973
49995
|
}
|
|
49974
49996
|
try {
|
|
49975
|
-
|
|
49976
|
-
try {
|
|
49977
|
-
files = import_node_fs2.default.readdirSync(at("keys"));
|
|
49978
|
-
} catch {
|
|
49979
|
-
files = [];
|
|
49980
|
-
}
|
|
49997
|
+
const files = listManagerKeyFiles();
|
|
49981
49998
|
const want = getAddress(address).toLowerCase();
|
|
49982
49999
|
const match = files.find((file) => isManagerKeyFile(file) && keystoreAddress(file)?.toLowerCase() === want);
|
|
49983
50000
|
if (!match) {
|
|
@@ -50610,8 +50627,9 @@ function startServer(sailDir, { port = PORT } = {}) {
|
|
|
50610
50627
|
const [permissionSigner, manager, , sessionActive] = configs;
|
|
50611
50628
|
const managerSet = Boolean(manager) && getAddress(manager) !== zeroAddress2;
|
|
50612
50629
|
const managerAddr = managerSet ? getAddress(manager) : localSigner;
|
|
50630
|
+
const knownManagerAddrs = (account.managers ?? []).map((a) => getAddress(a));
|
|
50613
50631
|
const signerAddrs = [
|
|
50614
|
-
...new Set([managerAddr, account.owner ? getAddress(account.owner) : null, ...localManagerAddrs].filter(Boolean))
|
|
50632
|
+
...new Set([managerAddr, account.owner ? getAddress(account.owner) : null, ...localManagerAddrs, ...knownManagerAddrs].filter(Boolean))
|
|
50615
50633
|
];
|
|
50616
50634
|
const balances = await Promise.all(signerAddrs.map((a) => client.getBalance({ address: a })));
|
|
50617
50635
|
const balanceByAddr = new Map(signerAddrs.map((a, i) => [a.toLowerCase(), balances[i]]));
|
|
@@ -50637,6 +50655,17 @@ function startServer(sailDir, { port = PORT } = {}) {
|
|
|
50637
50655
|
} else {
|
|
50638
50656
|
managerEntry = { role: "manager", address: null, balanceWei: null, balanceEth: null, status: "unconfigured" };
|
|
50639
50657
|
}
|
|
50658
|
+
if (knownManagerAddrs.length > 0) {
|
|
50659
|
+
const activeLower = (managerSet ? getAddress(manager) : account.manager ?? null)?.toLowerCase() ?? null;
|
|
50660
|
+
managerEntry.managers = knownManagerAddrs.map((a) => {
|
|
50661
|
+
const bal = balanceByAddr.get(a.toLowerCase());
|
|
50662
|
+
return {
|
|
50663
|
+
address: a,
|
|
50664
|
+
balanceEth: bal != null ? formatEther(bal) : null,
|
|
50665
|
+
isActive: a.toLowerCase() === activeLower
|
|
50666
|
+
};
|
|
50667
|
+
});
|
|
50668
|
+
}
|
|
50640
50669
|
const signers = [managerEntry];
|
|
50641
50670
|
for (const la of localManagerAddrs) {
|
|
50642
50671
|
if (getAddress(la) !== (managerAddr ? getAddress(managerAddr) : null)) {
|
|
@@ -50665,6 +50694,17 @@ function startServer(sailDir, { port = PORT } = {}) {
|
|
|
50665
50694
|
status: "local"
|
|
50666
50695
|
}));
|
|
50667
50696
|
}
|
|
50697
|
+
if (account.managers?.length > 0) {
|
|
50698
|
+
const onchainMgr = result.sma.manager;
|
|
50699
|
+
const activeLower = (onchainMgr && onchainMgr !== zeroAddress2 ? onchainMgr : account.manager ?? null)?.toLowerCase() ?? null;
|
|
50700
|
+
const managersPayload = account.managers.map((a) => ({
|
|
50701
|
+
address: getAddress(a),
|
|
50702
|
+
balanceEth: null,
|
|
50703
|
+
isActive: a.toLowerCase() === activeLower
|
|
50704
|
+
}));
|
|
50705
|
+
const mgrSigner = result.signers.find((s) => s.role === "manager");
|
|
50706
|
+
if (mgrSigner && !mgrSigner.managers) mgrSigner.managers = managersPayload;
|
|
50707
|
+
}
|
|
50668
50708
|
return result;
|
|
50669
50709
|
}
|
|
50670
50710
|
const distDir = process.env.SAILOR_UI_DIST ?? import_node_path.default.join(import_node_path.default.dirname(_thisFile), "dist");
|