@dev.sail.money/sailor 0.0.2-13 → 0.0.2-16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/packages/cli/dist/index.cjs +292 -11
- package/packages/sdk/dist/intelligence.d.ts +1 -1
- package/packages/sdk/dist/intelligence.js +1 -1
- package/packages/sdk/dist/templates/ammLiquidity.d.ts +24 -11
- package/packages/sdk/dist/templates/ammLiquidity.d.ts.map +1 -1
- package/packages/sdk/dist/templates/ammLiquidity.js +39 -31
- package/packages/sdk/dist/templates/ammLiquidity.js.map +1 -1
- package/packages/sdk/dist/templates/approveAndCallBatch.d.ts +24 -10
- package/packages/sdk/dist/templates/approveAndCallBatch.d.ts.map +1 -1
- package/packages/sdk/dist/templates/approveAndCallBatch.js +36 -23
- package/packages/sdk/dist/templates/approveAndCallBatch.js.map +1 -1
- package/packages/sdk/dist/templates/boundedBorrow.d.ts +19 -9
- package/packages/sdk/dist/templates/boundedBorrow.d.ts.map +1 -1
- package/packages/sdk/dist/templates/boundedBorrow.js +28 -19
- package/packages/sdk/dist/templates/boundedBorrow.js.map +1 -1
- package/packages/sdk/dist/templates/boundedSwap.d.ts +19 -9
- package/packages/sdk/dist/templates/boundedSwap.d.ts.map +1 -1
- package/packages/sdk/dist/templates/boundedSwap.js +30 -20
- package/packages/sdk/dist/templates/boundedSwap.js.map +1 -1
- package/packages/sdk/dist/templates/defiBundle.d.ts +35 -9
- package/packages/sdk/dist/templates/defiBundle.d.ts.map +1 -1
- package/packages/sdk/dist/templates/defiBundle.js +84 -22
- package/packages/sdk/dist/templates/defiBundle.js.map +1 -1
- package/packages/sdk/dist/templates/pendle.d.ts +23 -8
- package/packages/sdk/dist/templates/pendle.d.ts.map +1 -1
- package/packages/sdk/dist/templates/pendle.js +34 -14
- package/packages/sdk/dist/templates/pendle.js.map +1 -1
- package/packages/sdk/dist/templates/transferTarget.d.ts +11 -3
- package/packages/sdk/dist/templates/transferTarget.d.ts.map +1 -1
- package/packages/sdk/dist/templates/transferTarget.js +14 -7
- package/packages/sdk/dist/templates/transferTarget.js.map +1 -1
- package/packages/sdk/package.json +1 -0
package/package.json
CHANGED
|
@@ -40880,6 +40880,282 @@ Register it later with: sailor mandate attach --address ${deployed} --sma <SMA>`
|
|
|
40880
40880
|
attached: options.attach ? { sma: options.sma, txHash: attachTxHash } : null
|
|
40881
40881
|
});
|
|
40882
40882
|
}
|
|
40883
|
+
var CLONE_INIT_PREFIX = "0x3d602d80600a3d3981f3363d3d373d3d3d363d73";
|
|
40884
|
+
var CLONE_INIT_SUFFIX = "0x5af43d82803e903d91602b57fd5bf3";
|
|
40885
|
+
var PERMISSION_FACTORY_ABI = [
|
|
40886
|
+
{
|
|
40887
|
+
type: "function",
|
|
40888
|
+
name: "deployAndAttach",
|
|
40889
|
+
stateMutability: "payable",
|
|
40890
|
+
inputs: [
|
|
40891
|
+
{ name: "account", type: "address" },
|
|
40892
|
+
{ name: "impl", type: "address" },
|
|
40893
|
+
{ name: "salt", type: "bytes32" },
|
|
40894
|
+
{ name: "initData", type: "bytes" },
|
|
40895
|
+
{ name: "kernelDeadline", type: "uint256" },
|
|
40896
|
+
{ name: "kernelSig", type: "bytes" }
|
|
40897
|
+
],
|
|
40898
|
+
outputs: [{ name: "clone", type: "address" }]
|
|
40899
|
+
}
|
|
40900
|
+
];
|
|
40901
|
+
var CLONE_TEMPLATES = {
|
|
40902
|
+
boundedApprove: {
|
|
40903
|
+
label: "Bounded Approve",
|
|
40904
|
+
buildInitData: (p) => encodeFunctionData({
|
|
40905
|
+
abi: [
|
|
40906
|
+
{
|
|
40907
|
+
type: "function",
|
|
40908
|
+
name: "initialize",
|
|
40909
|
+
stateMutability: "nonpayable",
|
|
40910
|
+
inputs: [
|
|
40911
|
+
{ name: "allowedTokens", type: "address[]" },
|
|
40912
|
+
{ name: "allowedSpenders", type: "address[]" },
|
|
40913
|
+
{ name: "_maxAmountPerTx", type: "uint256" },
|
|
40914
|
+
{ name: "_permissionSigner", type: "address" }
|
|
40915
|
+
],
|
|
40916
|
+
outputs: []
|
|
40917
|
+
}
|
|
40918
|
+
],
|
|
40919
|
+
functionName: "initialize",
|
|
40920
|
+
args: [p.tokens, p.spenders, p.max, p.permissionSigner]
|
|
40921
|
+
}),
|
|
40922
|
+
describe: (p) => [
|
|
40923
|
+
{ label: "Allowed tokens", value: p.tokens.join(", ") },
|
|
40924
|
+
{ label: "Allowed spenders", value: p.spenders.join(", ") },
|
|
40925
|
+
{
|
|
40926
|
+
label: "Max approval / tx",
|
|
40927
|
+
value: p.max === maxUint256 ? "unlimited (uint256 max)" : p.max.toString()
|
|
40928
|
+
}
|
|
40929
|
+
]
|
|
40930
|
+
}
|
|
40931
|
+
};
|
|
40932
|
+
function predictCloneAddress(impl, factory, submitter, salt) {
|
|
40933
|
+
const namespacedSalt = keccak256(
|
|
40934
|
+
encodeAbiParameters([{ type: "address" }, { type: "bytes32" }], [submitter, salt])
|
|
40935
|
+
);
|
|
40936
|
+
const initCode = concatHex([CLONE_INIT_PREFIX, impl, CLONE_INIT_SUFFIX]);
|
|
40937
|
+
return getCreate2Address({
|
|
40938
|
+
from: factory,
|
|
40939
|
+
salt: namespacedSalt,
|
|
40940
|
+
bytecodeHash: keccak256(initCode)
|
|
40941
|
+
});
|
|
40942
|
+
}
|
|
40943
|
+
function parseAddressList(csv, flag) {
|
|
40944
|
+
if (!csv) throw new Error(`${flag} is required (comma-separated address list)`);
|
|
40945
|
+
const list = csv.split(",").map((s) => s.trim()).filter(Boolean);
|
|
40946
|
+
if (list.length === 0) throw new Error(`${flag} is empty`);
|
|
40947
|
+
for (const a of list) {
|
|
40948
|
+
if (!isAddress(a)) throw new Error(`${flag} contains an invalid address: ${a}`);
|
|
40949
|
+
}
|
|
40950
|
+
return list;
|
|
40951
|
+
}
|
|
40952
|
+
async function mandateDeployClone(options) {
|
|
40953
|
+
const project = requireProject();
|
|
40954
|
+
const channel = await createSigningChannel(process.cwd());
|
|
40955
|
+
try {
|
|
40956
|
+
await channel.start();
|
|
40957
|
+
await runDeployClone(project, channel, options);
|
|
40958
|
+
} catch (err) {
|
|
40959
|
+
fail(err, options.json);
|
|
40960
|
+
} finally {
|
|
40961
|
+
channel.stop();
|
|
40962
|
+
}
|
|
40963
|
+
}
|
|
40964
|
+
async function runDeployClone(project, channel, options) {
|
|
40965
|
+
const json = !!options.json;
|
|
40966
|
+
const say = (fn) => {
|
|
40967
|
+
if (!json) fn();
|
|
40968
|
+
};
|
|
40969
|
+
if (!isAddress(options.sma)) throw new Error(`Invalid --sma address: ${options.sma}`);
|
|
40970
|
+
const sma = options.sma;
|
|
40971
|
+
const spec = CLONE_TEMPLATES[options.template];
|
|
40972
|
+
if (!spec) {
|
|
40973
|
+
throw new Error(
|
|
40974
|
+
`Unsupported clone template "${options.template}". Supported: ${Object.keys(CLONE_TEMPLATES).join(", ")}`
|
|
40975
|
+
);
|
|
40976
|
+
}
|
|
40977
|
+
const impl = project.deployment.standaloneTemplates?.[options.template];
|
|
40978
|
+
if (!impl || !isAddress(impl)) {
|
|
40979
|
+
throw new Error(
|
|
40980
|
+
`No "${options.template}" standalone template is bundled for chain ${project.chainId}.`
|
|
40981
|
+
);
|
|
40982
|
+
}
|
|
40983
|
+
const chain2 = getChainById(project.chainId);
|
|
40984
|
+
const publicClient = publicClientFor(project);
|
|
40985
|
+
const agentSigner = await loadManagerSigner2();
|
|
40986
|
+
const registered = await publicClient.readContract({
|
|
40987
|
+
address: project.contracts.kernel,
|
|
40988
|
+
abi: SailKernelAbi,
|
|
40989
|
+
functionName: "registered",
|
|
40990
|
+
args: [sma]
|
|
40991
|
+
});
|
|
40992
|
+
if (!registered) {
|
|
40993
|
+
throw new Error(`SMA ${sma} is not registered with SailKernel; cannot register a permission.`);
|
|
40994
|
+
}
|
|
40995
|
+
const kernelConfig = await publicClient.readContract({
|
|
40996
|
+
address: project.contracts.kernel,
|
|
40997
|
+
abi: SailKernelAbi,
|
|
40998
|
+
functionName: "configs",
|
|
40999
|
+
args: [sma]
|
|
41000
|
+
});
|
|
41001
|
+
const permissionSigner = kernelConfig[0];
|
|
41002
|
+
const initParams = {
|
|
41003
|
+
permissionSigner,
|
|
41004
|
+
tokens: parseAddressList(options.tokens, "--tokens"),
|
|
41005
|
+
spenders: parseAddressList(options.spenders, "--spenders"),
|
|
41006
|
+
max: options.max ? BigInt(options.max) : maxUint256
|
|
41007
|
+
};
|
|
41008
|
+
const initData = spec.buildInitData(initParams);
|
|
41009
|
+
const submitter = agentSigner.address;
|
|
41010
|
+
const salt = keccak256(
|
|
41011
|
+
encodeAbiParameters(
|
|
41012
|
+
[{ type: "address" }, { type: "address" }, { type: "uint256" }],
|
|
41013
|
+
[sma, impl, BigInt(Math.floor(Date.now() / 1e3))]
|
|
41014
|
+
)
|
|
41015
|
+
);
|
|
41016
|
+
const clone = predictCloneAddress(impl, project.contracts.permissionFactory, submitter, salt);
|
|
41017
|
+
say(() => {
|
|
41018
|
+
console.log(`
|
|
41019
|
+
${spec.label} clone (${options.template})`);
|
|
41020
|
+
console.log(` logic impl: ${impl}`);
|
|
41021
|
+
console.log(` predicted clone: ${clone}`);
|
|
41022
|
+
console.log(` SMA: ${sma}`);
|
|
41023
|
+
for (const d of spec.describe(initParams)) console.log(` ${d.label}: ${d.value}`);
|
|
41024
|
+
console.log(`
|
|
41025
|
+
\u2192 Signing station:
|
|
41026
|
+
Open ${channel.url} and connect your Owner wallet
|
|
41027
|
+
`);
|
|
41028
|
+
});
|
|
41029
|
+
const nonce = await publicClient.readContract({
|
|
41030
|
+
address: project.contracts.kernel,
|
|
41031
|
+
abi: SailKernelAbi,
|
|
41032
|
+
functionName: "signerNonces",
|
|
41033
|
+
args: [sma]
|
|
41034
|
+
});
|
|
41035
|
+
let registerPermissionHasDeadline = false;
|
|
41036
|
+
try {
|
|
41037
|
+
const caps = await detectKernelCapabilities(publicClient, project.contracts.kernel, {
|
|
41038
|
+
chainId: project.chainId
|
|
41039
|
+
});
|
|
41040
|
+
registerPermissionHasDeadline = caps.registerPermissionHasDeadline;
|
|
41041
|
+
} catch {
|
|
41042
|
+
}
|
|
41043
|
+
const deadline = registerPermissionHasDeadline ? BigInt(Math.floor(Date.now() / 1e3) + 300) : void 0;
|
|
41044
|
+
const typedData = buildRegisterPermissionTypedData({
|
|
41045
|
+
chainId: project.chainId,
|
|
41046
|
+
kernel: project.contracts.kernel,
|
|
41047
|
+
account: sma,
|
|
41048
|
+
permission: clone,
|
|
41049
|
+
nonce,
|
|
41050
|
+
hasDeadline: registerPermissionHasDeadline,
|
|
41051
|
+
deadline
|
|
41052
|
+
});
|
|
41053
|
+
const label = options.label ?? `${spec.label} (${options.template})`;
|
|
41054
|
+
say(
|
|
41055
|
+
() => console.log(
|
|
41056
|
+
`Pushing signing request \u2014 the mandate signer (${permissionSigner}) must sign in the browser.`
|
|
41057
|
+
)
|
|
41058
|
+
);
|
|
41059
|
+
const response = await channel.requestSignature({
|
|
41060
|
+
type: "typed-data",
|
|
41061
|
+
kind: "register-permission",
|
|
41062
|
+
title: `Authorize "${label}"`,
|
|
41063
|
+
description: `Sign to authorize a new ${spec.label} permission on your SMA. The agent deploys and registers it in one transaction.`,
|
|
41064
|
+
chainId: project.chainId,
|
|
41065
|
+
details: [
|
|
41066
|
+
{ label: "SMA", value: sma },
|
|
41067
|
+
{ label: "Permission (predicted)", value: clone },
|
|
41068
|
+
{ label: "Template", value: options.template },
|
|
41069
|
+
{ label: "Mandate signer", value: permissionSigner },
|
|
41070
|
+
...spec.describe(initParams)
|
|
41071
|
+
],
|
|
41072
|
+
typedData
|
|
41073
|
+
});
|
|
41074
|
+
if (response.status === "rejected") {
|
|
41075
|
+
throw new Error(`User rejected authorization: ${response.reason ?? "no reason given"}`);
|
|
41076
|
+
}
|
|
41077
|
+
if (response.status !== "signature") {
|
|
41078
|
+
throw new Error(`Expected EIP-712 signature response, got: ${response.status}`);
|
|
41079
|
+
}
|
|
41080
|
+
const signature = response.signature;
|
|
41081
|
+
try {
|
|
41082
|
+
const recoveredSigner = await recoverTypedDataAddress({
|
|
41083
|
+
domain: sailKernelDomain({ chainId: project.chainId, kernel: project.contracts.kernel }),
|
|
41084
|
+
types: registerPermissionHasDeadline ? REGISTER_PERMISSION_TYPES : REGISTER_PERMISSION_TYPES_NO_DEADLINE,
|
|
41085
|
+
primaryType: "RegisterPermission",
|
|
41086
|
+
message: registerPermissionHasDeadline ? { account: sma, permission: clone, nonce, deadline } : { account: sma, permission: clone, nonce },
|
|
41087
|
+
signature
|
|
41088
|
+
});
|
|
41089
|
+
if (recoveredSigner.toLowerCase() !== permissionSigner.toLowerCase()) {
|
|
41090
|
+
throw new Error(
|
|
41091
|
+
`Security: RegisterPermission was signed by ${recoveredSigner} but the on-chain mandate signer is ${permissionSigner}.
|
|
41092
|
+
Connect the owner wallet (mandate signer) in the browser \u2014 the agent wallet must never sign permission registrations.`
|
|
41093
|
+
);
|
|
41094
|
+
}
|
|
41095
|
+
} catch (err) {
|
|
41096
|
+
if (err.message.startsWith("Security:")) throw err;
|
|
41097
|
+
}
|
|
41098
|
+
const fee = await estimatePermissionFee(publicClient, project.contracts.governance, clone);
|
|
41099
|
+
if (!registerPermissionHasDeadline || deadline === void 0) {
|
|
41100
|
+
throw new Error(
|
|
41101
|
+
"deploy-clone requires a selective kernel (RegisterPermission with deadline). This chain's kernel does not match."
|
|
41102
|
+
);
|
|
41103
|
+
}
|
|
41104
|
+
say(() => console.log(`Submitting deployAndAttach (agent pays gas; fee ${fee} wei)\u2026`));
|
|
41105
|
+
const walletClient = createWalletClient({
|
|
41106
|
+
account: agentSigner.viemAccount,
|
|
41107
|
+
chain: chain2,
|
|
41108
|
+
transport: http(getRpcUrl(project.chainId))
|
|
41109
|
+
});
|
|
41110
|
+
const data = encodeFunctionData({
|
|
41111
|
+
abi: PERMISSION_FACTORY_ABI,
|
|
41112
|
+
functionName: "deployAndAttach",
|
|
41113
|
+
args: [sma, impl, salt, initData, deadline, signature]
|
|
41114
|
+
});
|
|
41115
|
+
const txHash = await walletClient.sendTransaction({
|
|
41116
|
+
to: project.contracts.permissionFactory,
|
|
41117
|
+
data,
|
|
41118
|
+
value: fee,
|
|
41119
|
+
account: agentSigner.viemAccount,
|
|
41120
|
+
chain: chain2
|
|
41121
|
+
});
|
|
41122
|
+
say(() => console.log("Waiting for confirmation\u2026"));
|
|
41123
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: txHash });
|
|
41124
|
+
if (receipt.status !== "success") {
|
|
41125
|
+
throw new Error(`deployAndAttach reverted (tx ${txHash})`);
|
|
41126
|
+
}
|
|
41127
|
+
const attached = await pollForPermission(publicClient, project.contracts.kernel, sma, clone);
|
|
41128
|
+
if (!attached) {
|
|
41129
|
+
throw new Error(
|
|
41130
|
+
`Tx ${txHash} mined, but clone ${clone} is not in getPermissions(${sma}). Verify on-chain.`
|
|
41131
|
+
);
|
|
41132
|
+
}
|
|
41133
|
+
say(() => console.log("\u2713", `Deployed + registered ${spec.label} at ${clone}`));
|
|
41134
|
+
const store = new MandateStore();
|
|
41135
|
+
store.add({
|
|
41136
|
+
name: label,
|
|
41137
|
+
address: clone,
|
|
41138
|
+
txHash,
|
|
41139
|
+
chainId: project.chainId,
|
|
41140
|
+
deployedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
41141
|
+
});
|
|
41142
|
+
store.recordAttachment(clone, { sma, txHash });
|
|
41143
|
+
appendActivity({
|
|
41144
|
+
ts: nowIso(),
|
|
41145
|
+
actor: "agent",
|
|
41146
|
+
type: "permission_registered",
|
|
41147
|
+
permission: clone,
|
|
41148
|
+
name: label,
|
|
41149
|
+
sma,
|
|
41150
|
+
txHash,
|
|
41151
|
+
chainId: project.chainId
|
|
41152
|
+
});
|
|
41153
|
+
emit(json, () => {
|
|
41154
|
+
}, {
|
|
41155
|
+
status: "ok",
|
|
41156
|
+
clone: { template: options.template, address: clone, impl, txHash, sma, chainId: project.chainId }
|
|
41157
|
+
});
|
|
41158
|
+
}
|
|
40883
41159
|
async function mandateAttach(options) {
|
|
40884
41160
|
const project = requireProject();
|
|
40885
41161
|
const channel = await createSigningChannel(process.cwd());
|
|
@@ -42114,7 +42390,8 @@ Configure the chain in @sail/chains or set KERNEL_ADDRESS in .sail/.env.local.`
|
|
|
42114
42390
|
} catch (err) {
|
|
42115
42391
|
console.error(`could not read registered permissions: ${err.message}`);
|
|
42116
42392
|
}
|
|
42117
|
-
let
|
|
42393
|
+
let tickExecuted = 0;
|
|
42394
|
+
let tickReverted = 0;
|
|
42118
42395
|
let tickSkipped = 0;
|
|
42119
42396
|
for (const rawDispatch of dispatches) {
|
|
42120
42397
|
const dispatch = rawDispatch;
|
|
@@ -42193,9 +42470,15 @@ Configure the chain in @sail/chains or set KERNEL_ADDRESS in .sail/.env.local.`
|
|
|
42193
42470
|
} catch {
|
|
42194
42471
|
}
|
|
42195
42472
|
}
|
|
42196
|
-
|
|
42197
|
-
|
|
42198
|
-
|
|
42473
|
+
if (!result.success) {
|
|
42474
|
+
appendActivity({ ts: nowIso(), actor: "agent", type: "dispatch_reverted", permission, target, txHash: result.txHash, gasUsed: String(result.gasUsed) });
|
|
42475
|
+
console.error(`reverted: ${result.txHash} (gas used: ${result.gasUsed})`);
|
|
42476
|
+
tickReverted++;
|
|
42477
|
+
} else {
|
|
42478
|
+
appendActivity({ ts: nowIso(), actor: "agent", type: "dispatch_executed", permission, target, txHash: result.txHash });
|
|
42479
|
+
console.log(`executed: ${result.txHash}`);
|
|
42480
|
+
tickExecuted++;
|
|
42481
|
+
}
|
|
42199
42482
|
} catch (err) {
|
|
42200
42483
|
const reason = err.message;
|
|
42201
42484
|
console.error(`dispatch error: ${reason}`);
|
|
@@ -42204,13 +42487,10 @@ Configure the chain in @sail/chains or set KERNEL_ADDRESS in .sail/.env.local.`
|
|
|
42204
42487
|
}
|
|
42205
42488
|
}
|
|
42206
42489
|
if (dispatches.length > 0) {
|
|
42207
|
-
|
|
42208
|
-
|
|
42209
|
-
|
|
42210
|
-
|
|
42211
|
-
} else {
|
|
42212
|
-
console.log(`tick complete: ${tickDispatched} dispatched`);
|
|
42213
|
-
}
|
|
42490
|
+
const parts = [`${tickExecuted} executed`];
|
|
42491
|
+
if (tickReverted > 0) parts.push(`${tickReverted} reverted`);
|
|
42492
|
+
if (tickSkipped > 0) parts.push(`${tickSkipped} skipped`);
|
|
42493
|
+
console.log(`tick complete: ${parts.join(", ")}`);
|
|
42214
42494
|
}
|
|
42215
42495
|
appendActivity({ ts: nowIso(), actor: "agent", type: "tick_end" });
|
|
42216
42496
|
}
|
|
@@ -42707,6 +42987,7 @@ mandate.command("prepare").description("Prepare a mandate draft for review and s
|
|
|
42707
42987
|
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));
|
|
42708
42988
|
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 a JSON array, e.g. '[["0x.."],"1000"]'`).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));
|
|
42709
42989
|
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));
|
|
42990
|
+
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));
|
|
42710
42991
|
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));
|
|
42711
42992
|
mandate.command("templates").description("Show how to author your own permission contract (and any community-deployed addresses)").option("--json", "Emit machine-readable JSON").action(actionWith(mandateTemplates));
|
|
42712
42993
|
mandate.command("list").description("List permission contracts deployed from this project").action(action(async () => mandateContractsList()));
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Do not edit manually — run `pnpm build` to regenerate.
|
|
6
6
|
*
|
|
7
7
|
* Spec version : 1.2.0
|
|
8
|
-
* Generated at : 2026-06-
|
|
8
|
+
* Generated at : 2026-06-06T19:10:39.495Z
|
|
9
9
|
*/
|
|
10
10
|
export declare const SAIL_INTELLIGENCE_BASE_URL = "https://api.sail.money";
|
|
11
11
|
export declare const SAIL_INTELLIGENCE_DOCS_URL = "https://api.sail.money/docs";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Do not edit manually — run `pnpm build` to regenerate.
|
|
6
6
|
*
|
|
7
7
|
* Spec version : 1.2.0
|
|
8
|
-
* Generated at : 2026-06-
|
|
8
|
+
* Generated at : 2026-06-06T19:10:39.495Z
|
|
9
9
|
*/
|
|
10
10
|
export const SAIL_INTELLIGENCE_BASE_URL = "https://api.sail.money";
|
|
11
11
|
export const SAIL_INTELLIGENCE_DOCS_URL = "https://api.sail.money/docs";
|
|
@@ -1,16 +1,29 @@
|
|
|
1
1
|
import type { Address, PermissionTemplate } from "../types.js";
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Params for SharedAMMLiquidityPermission.
|
|
4
|
+
*
|
|
5
|
+
* Matches the on-chain `_applyConfig` decode exactly:
|
|
6
|
+
* abi.decode(params, (address[], address[], uint128, bool, bool, bool, bool, bool))
|
|
7
|
+
* → allowedTargets, allowedTokens, maxAmountPerTokenPerTx,
|
|
8
|
+
* allowMint, allowIncrease, allowDecrease, allowCollect, allowBurn
|
|
9
|
+
*/
|
|
3
10
|
export type AmmLiquidityParams = {
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
|
|
11
|
+
/** Position-manager / router addresses the agent may call (UniV3 NPM, Aerodrome, …). */
|
|
12
|
+
allowedTargets: Address[];
|
|
13
|
+
/** Token addresses the agent may provide as liquidity. */
|
|
14
|
+
allowedTokens: Address[];
|
|
15
|
+
/** Maximum amount of any single token per tx, in that token's base units (uint128). */
|
|
16
|
+
maxAmountPerTokenPerTx: bigint;
|
|
17
|
+
/** Allow mint / open-position (and Aerodrome add-liquidity). */
|
|
18
|
+
allowMint: boolean;
|
|
19
|
+
/** Allow increaseLiquidity. */
|
|
20
|
+
allowIncrease: boolean;
|
|
21
|
+
/** Allow decreaseLiquidity (and Aerodrome remove-liquidity). */
|
|
22
|
+
allowDecrease: boolean;
|
|
23
|
+
/** Allow collect (fee withdrawal). */
|
|
24
|
+
allowCollect: boolean;
|
|
25
|
+
/** Allow burn (close position NFT). */
|
|
26
|
+
allowBurn: boolean;
|
|
14
27
|
};
|
|
15
28
|
export declare const ammLiquidityTemplate: PermissionTemplate<AmmLiquidityParams>;
|
|
16
29
|
//# sourceMappingURL=ammLiquidity.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ammLiquidity.d.ts","sourceRoot":"","sources":["../../src/templates/ammLiquidity.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAA2B,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAExF
|
|
1
|
+
{"version":3,"file":"ammLiquidity.d.ts","sourceRoot":"","sources":["../../src/templates/ammLiquidity.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAA2B,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAExF;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,wFAAwF;IACxF,cAAc,EAAE,OAAO,EAAE,CAAC;IAC1B,0DAA0D;IAC1D,aAAa,EAAE,OAAO,EAAE,CAAC;IACzB,uFAAuF;IACvF,sBAAsB,EAAE,MAAM,CAAC;IAC/B,gEAAgE;IAChE,SAAS,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,aAAa,EAAE,OAAO,CAAC;IACvB,gEAAgE;IAChE,aAAa,EAAE,OAAO,CAAC;IACvB,sCAAsC;IACtC,YAAY,EAAE,OAAO,CAAC;IACtB,uCAAuC;IACvC,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAaF,eAAO,MAAM,oBAAoB,EAAE,kBAAkB,CAAC,kBAAkB,CAyDvE,CAAC"}
|
|
@@ -1,56 +1,64 @@
|
|
|
1
1
|
import { decodeAbiParameters, encodeAbiParameters } from "viem";
|
|
2
2
|
const ABI = [
|
|
3
|
-
{ name: "
|
|
4
|
-
{ name: "
|
|
5
|
-
{ name: "
|
|
6
|
-
{ name: "
|
|
7
|
-
{ name: "
|
|
3
|
+
{ name: "allowedTargets", type: "address[]" },
|
|
4
|
+
{ name: "allowedTokens", type: "address[]" },
|
|
5
|
+
{ name: "maxAmountPerTokenPerTx", type: "uint128" },
|
|
6
|
+
{ name: "allowMint", type: "bool" },
|
|
7
|
+
{ name: "allowIncrease", type: "bool" },
|
|
8
|
+
{ name: "allowDecrease", type: "bool" },
|
|
9
|
+
{ name: "allowCollect", type: "bool" },
|
|
10
|
+
{ name: "allowBurn", type: "bool" },
|
|
8
11
|
];
|
|
9
12
|
export const ammLiquidityTemplate = {
|
|
10
|
-
name: "
|
|
13
|
+
name: "SharedAMMLiquidityPermission",
|
|
11
14
|
address: "0x0000000000000000000000000000000000000000",
|
|
12
15
|
encoder: {
|
|
13
16
|
encode(params) {
|
|
14
17
|
return encodeAbiParameters(ABI, [
|
|
15
|
-
|
|
16
|
-
params.
|
|
17
|
-
params.
|
|
18
|
-
params.
|
|
19
|
-
|
|
18
|
+
params.allowedTargets,
|
|
19
|
+
params.allowedTokens,
|
|
20
|
+
params.maxAmountPerTokenPerTx,
|
|
21
|
+
params.allowMint,
|
|
22
|
+
params.allowIncrease,
|
|
23
|
+
params.allowDecrease,
|
|
24
|
+
params.allowCollect,
|
|
25
|
+
params.allowBurn,
|
|
20
26
|
]);
|
|
21
27
|
},
|
|
22
28
|
decode(data) {
|
|
23
29
|
const decoded = decodeAbiParameters(ABI, data);
|
|
24
|
-
const maxRangeBps = Number(decoded[4]);
|
|
25
30
|
return {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
allowedTargets: [...decoded[0]],
|
|
32
|
+
allowedTokens: [...decoded[1]],
|
|
33
|
+
maxAmountPerTokenPerTx: decoded[2],
|
|
34
|
+
allowMint: decoded[3],
|
|
35
|
+
allowIncrease: decoded[4],
|
|
36
|
+
allowDecrease: decoded[5],
|
|
37
|
+
allowCollect: decoded[6],
|
|
38
|
+
allowBurn: decoded[7],
|
|
31
39
|
};
|
|
32
40
|
},
|
|
33
41
|
},
|
|
34
42
|
explainer: {
|
|
35
43
|
explain(params) {
|
|
36
44
|
const warnings = [];
|
|
37
|
-
if (params.
|
|
38
|
-
warnings.push("
|
|
45
|
+
if (params.allowedTargets.length === 0) {
|
|
46
|
+
warnings.push("No targets specified — all liquidity operations will be blocked");
|
|
39
47
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
const ops = [
|
|
49
|
+
params.allowMint && "mint",
|
|
50
|
+
params.allowIncrease && "increase",
|
|
51
|
+
params.allowDecrease && "decrease",
|
|
52
|
+
params.allowCollect && "collect",
|
|
53
|
+
params.allowBurn && "burn",
|
|
54
|
+
].filter(Boolean);
|
|
46
55
|
return {
|
|
47
|
-
templateName: "
|
|
56
|
+
templateName: "SharedAMMLiquidityPermission",
|
|
48
57
|
humanReadable: [
|
|
49
|
-
`Maximum
|
|
50
|
-
`Allowed
|
|
51
|
-
`Allowed
|
|
52
|
-
`
|
|
53
|
-
...(rangeNote ? [rangeNote] : []),
|
|
58
|
+
`Maximum amount per token per tx: ${params.maxAmountPerTokenPerTx.toString()} (base units)`,
|
|
59
|
+
`Allowed targets (${params.allowedTargets.length}): ${params.allowedTargets.join(", ")}`,
|
|
60
|
+
`Allowed tokens: ${params.allowedTokens.join(", ")}`,
|
|
61
|
+
`Enabled operations: ${ops.length ? ops.join(", ") : "none"}`,
|
|
54
62
|
],
|
|
55
63
|
warnings,
|
|
56
64
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ammLiquidity.js","sourceRoot":"","sources":["../../src/templates/ammLiquidity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"ammLiquidity.js","sourceRoot":"","sources":["../../src/templates/ammLiquidity.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,14 +1,28 @@
|
|
|
1
|
-
import type { Address, PermissionTemplate } from "../types.js";
|
|
2
|
-
/**
|
|
1
|
+
import type { Address, Hex, PermissionTemplate } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Params for SharedApproveAndCallBatchPermission.
|
|
4
|
+
*
|
|
5
|
+
* Matches the on-chain `_applyConfig` decode exactly:
|
|
6
|
+
* abi.decode(params, (Config))
|
|
7
|
+
* where Config is the tuple
|
|
8
|
+
* (address[] tokens, address[] spenders, address[] consumingTargets,
|
|
9
|
+
* bytes4[] consumingSelectors, uint256[] maxApprovalAmounts, bool requireAmountMatch)
|
|
10
|
+
*
|
|
11
|
+
* Note: the contract encodes a SINGLE struct, so the blob is one top-level tuple.
|
|
12
|
+
*/
|
|
3
13
|
export type ApproveAndCallBatchParams = {
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
|
|
14
|
+
/** Allowlisted ERC-20 tokens that may be approved. Index-parallel with `maxApprovalAmounts`. */
|
|
15
|
+
tokens: Address[];
|
|
16
|
+
/** Allowlisted spenders that may receive the allowance. */
|
|
17
|
+
spenders: Address[];
|
|
18
|
+
/** Allowlisted consuming-call targets (often the same as spenders). */
|
|
19
|
+
consumingTargets: Address[];
|
|
20
|
+
/** Allowlisted selectors for the consuming call (4-byte hex, e.g. "0x095ea7b3"). */
|
|
21
|
+
consumingSelectors: Hex[];
|
|
22
|
+
/** Max approve amount per token, index-parallel with `tokens`. */
|
|
23
|
+
maxApprovalAmounts: bigint[];
|
|
24
|
+
/** When true, the consuming call's leading uint256 arg must equal the approved amount. */
|
|
25
|
+
requireAmountMatch: boolean;
|
|
12
26
|
};
|
|
13
27
|
export declare const approveAndCallBatchTemplate: PermissionTemplate<ApproveAndCallBatchParams>;
|
|
14
28
|
//# sourceMappingURL=approveAndCallBatch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approveAndCallBatch.d.ts","sourceRoot":"","sources":["../../src/templates/approveAndCallBatch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"approveAndCallBatch.d.ts","sourceRoot":"","sources":["../../src/templates/approveAndCallBatch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAsB,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAExF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,gGAAgG;IAChG,MAAM,EAAE,OAAO,EAAE,CAAC;IAClB,2DAA2D;IAC3D,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,uEAAuE;IACvE,gBAAgB,EAAE,OAAO,EAAE,CAAC;IAC5B,oFAAoF;IACpF,kBAAkB,EAAE,GAAG,EAAE,CAAC;IAC1B,kEAAkE;IAClE,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,0FAA0F;IAC1F,kBAAkB,EAAE,OAAO,CAAC;CAC7B,CAAC;AAaF,eAAO,MAAM,2BAA2B,EAAE,kBAAkB,CAAC,yBAAyB,CA4DrF,CAAC"}
|