import type { Address, Hex } from "viem"; import type { LucidlyClient } from "./client.js"; import type { TxResult } from "./types.js"; /** * LucidlyPolicyManager — one instance per vault, deployed by the factory. Gives the vault's * STRATEGIST the ability to self-enable policies the Registry has whitelisted (no Lucidly signature * needed), and to publicly request non-whitelisted ones for owner review. This is the only path by * which a strategist's manage root changes. * * SDK surface: * • add() — addRegistryPolicy(templateId, packedArgs) → strategist only * • remove() — removePolicy(leaf) → strategist only * • request() — requestPolicy(protocolId, target, selector, argsHash, note) → strategist only * • root() — current Merkle root the strategist's vault enforces * • count() — number of enabled leaves * • clones() — per-protocol {decoder, tvlAdapter, oracle} this vault is bound to */ export const POLICY_MANAGER_ABI = [ // strategist writes { type: "function", name: "addRegistryPolicy", stateMutability: "nonpayable", inputs: [{ name: "templateId", type: "bytes32" }, { name: "packedArgs", type: "bytes" }], outputs: [{ type: "bytes32" }], }, { type: "function", name: "removePolicy", stateMutability: "nonpayable", inputs: [{ name: "leaf", type: "bytes32" }], outputs: [], }, { type: "function", name: "requestPolicy", stateMutability: "nonpayable", inputs: [ { name: "protocolId", type: "bytes32" }, { name: "target", type: "address" }, { name: "selector", type: "bytes4" }, { name: "argsHash", type: "bytes32" }, { name: "note", type: "string" }, ], outputs: [], }, // reads { type: "function", name: "currentRoot", stateMutability: "view", inputs: [], outputs: [{ type: "bytes32" }] }, { type: "function", name: "enabledCount", stateMutability: "view", inputs: [], outputs: [{ type: "uint256" }] }, { type: "function", name: "enabledLeavesList", stateMutability: "view", inputs: [], outputs: [{ type: "bytes32[]" }] }, { type: "function", name: "usedProtocolsList", stateMutability: "view", inputs: [], outputs: [{ type: "bytes32[]" }] }, { type: "function", name: "strategist", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] }, { type: "function", name: "vault", stateMutability: "view", inputs: [], outputs: [{ type: "address" }] }, { type: "function", name: "clonesOf", stateMutability: "view", inputs: [{ name: "protocolId", type: "bytes32" }], outputs: [ { name: "decoder", type: "address" }, { name: "tvlAdapter", type: "address" }, { name: "oracle", type: "address" }, { name: "exists", type: "bool" }, ], }, ] as const; export interface PolicyClones { decoder: Address; tvlAdapter: Address; oracle: Address; exists: boolean; } export class PolicyManagerModule { constructor(private readonly client: LucidlyClient) {} // ---- writes ---- /** addRegistryPolicy(templateId, packedArgs). Returns tx hash + a wait() helper. */ async add(opts: { policyManager: Address; templateId: Hex; packedArgs: Hex }): Promise { const hash = await this.client.send({ address: opts.policyManager, abi: POLICY_MANAGER_ABI, functionName: "addRegistryPolicy", args: [opts.templateId, opts.packedArgs], }); return this.client.wrapTx(hash); } async remove(opts: { policyManager: Address; leaf: Hex }): Promise { const hash = await this.client.send({ address: opts.policyManager, abi: POLICY_MANAGER_ABI, functionName: "removePolicy", args: [opts.leaf], }); return this.client.wrapTx(hash); } /** Publicly request a non-whitelisted policy → emits PolicyRequested → indexer review queue. */ async request(opts: { policyManager: Address; protocolId: Hex; target: Address; selector: Hex; argsHash: Hex; note: string; }): Promise { const hash = await this.client.send({ address: opts.policyManager, abi: POLICY_MANAGER_ABI, functionName: "requestPolicy", args: [opts.protocolId, opts.target, opts.selector, opts.argsHash, opts.note], }); return this.client.wrapTx(hash); } // ---- reads ---- async root(policyManager: Address): Promise { return (await this.client.read({ address: policyManager, abi: POLICY_MANAGER_ABI, functionName: "currentRoot", })) as Hex; } async count(policyManager: Address): Promise { return Number( (await this.client.read({ address: policyManager, abi: POLICY_MANAGER_ABI, functionName: "enabledCount", })) as bigint, ); } async leaves(policyManager: Address): Promise { return (await this.client.read({ address: policyManager, abi: POLICY_MANAGER_ABI, functionName: "enabledLeavesList", })) as readonly Hex[]; } async usedProtocols(policyManager: Address): Promise { return (await this.client.read({ address: policyManager, abi: POLICY_MANAGER_ABI, functionName: "usedProtocolsList", })) as readonly Hex[]; } async strategist(policyManager: Address): Promise
{ return (await this.client.read({ address: policyManager, abi: POLICY_MANAGER_ABI, functionName: "strategist", })) as Address; } async clones(policyManager: Address, protocolId: Hex): Promise { const tup = (await this.client.read({ address: policyManager, abi: POLICY_MANAGER_ABI, functionName: "clonesOf", args: [protocolId], })) as readonly [Address, Address, Address, boolean]; return { decoder: tup[0], tvlAdapter: tup[1], oracle: tup[2], exists: tup[3] }; } }