import type { BaseContract } from "ethers"; import { getAddress, isAddress } from "ethers"; import { TargetConfig, resolveAdmin } from "./config"; export interface AuthorityDecision { mode: "execute" | "calldata"; signer: string; configuredAdmin: string; chainAdmin?: string; executor?: string; reason: string; } export const assertSelectedSimulationSignerAuthorized = ( decision: AuthorityDecision, stepId: string, ): void => { if (decision.mode === "execute") return; const requiredExecutor = decision.executor || decision.configuredAdmin; throw new Error( `selected simulation signer is not authorized for step ${stepId}; required executor ${requiredExecutor}`, ); }; const readChainAdmin = async (contract: BaseContract): Promise => { const callable = contract as unknown as Record; for (const methodName of ["owner", "admin"]) { const method = callable[methodName]; if (typeof method !== "function") continue; try { const value = await (method as () => Promise)(); if (typeof value === "string" && isAddress(value)) { return getAddress(value); } } catch { // Some proxy admin methods intentionally revert for non-admin callers. } } return undefined; }; interface ControlledMulticallAuthorityContract { callers: (caller: string) => Promise; factoryCallers: (factory: string, caller: string) => Promise; owner?: () => Promise; admin?: () => Promise; } export const decideControlledMulticallAuthority = async ( contract: ControlledMulticallAuthorityContract, factory: string, target: TargetConfig, signerAddress: string, ): Promise => { const normalizedSignerAddress = getAddress(signerAddress); const normalizedFactory = getAddress(factory); let callerAllowed: boolean; let factoryAllowed: boolean; try { [callerAllowed, factoryAllowed] = await Promise.all([ contract.callers(normalizedSignerAddress), contract.factoryCallers(normalizedFactory, normalizedSignerAddress), ]); } catch (error) { throw new Error( "Cannot determine controlled multicall authority: " + (error instanceof Error ? error.message : String(error)), ); } const configuredAdmin = resolveAdmin(target, normalizedSignerAddress); const chainAdmin = await readChainAdmin(contract as unknown as BaseContract); if (callerAllowed && factoryAllowed) { return { mode: "execute", signer: normalizedSignerAddress, configuredAdmin, chainAdmin, reason: "operator has controlled multicall caller and factory authority", }; } const explicitConfiguredAdmin = typeof target.admin === "string" && isAddress(target.admin) ? getAddress(target.admin) : undefined; const executor = chainAdmin || explicitConfiguredAdmin; if (!executor || executor.toLowerCase() === normalizedSignerAddress.toLowerCase()) { throw new Error( "Controlled multicall permissions are missing and cannot resolve an external executor", ); } return { mode: "calldata", signer: normalizedSignerAddress, configuredAdmin, chainAdmin, executor, reason: [ "controlled multicall authority requires external execution", callerAllowed ? undefined : "caller permission missing", factoryAllowed ? undefined : "factory caller permission missing", ].filter(Boolean).join("; "), }; }; export const decideAuthority = async ( contract: BaseContract, target: TargetConfig, signerAddress: string, ): Promise => { const normalizedSignerAddress = getAddress(signerAddress); const configuredAdmin = resolveAdmin(target, normalizedSignerAddress); const chainAdmin = await readChainAdmin(contract); if (chainAdmin) { if (chainAdmin.toLowerCase() === normalizedSignerAddress.toLowerCase()) { return { mode: "execute", signer: normalizedSignerAddress, configuredAdmin, chainAdmin, reason: "signer is chain admin", }; } return { mode: "calldata", signer: normalizedSignerAddress, configuredAdmin, chainAdmin, executor: chainAdmin, reason: "signer is not chain admin", }; } if (configuredAdmin.toLowerCase() !== normalizedSignerAddress.toLowerCase()) { return { mode: "calldata", signer: normalizedSignerAddress, configuredAdmin, executor: configuredAdmin, reason: "configured admin differs from signer", }; } return { mode: "execute", signer: normalizedSignerAddress, configuredAdmin, reason: "no chain admin check available", }; };