@moesi/settle-zerodev
v0.4.0
Published
ZeroDev session-key settlement adapter for Moesi — implements the abstract Settlement interface over @zerodev/sdk + @zerodev/permissions.
Maintainers
Readme
@moesi/settle-zerodev
ZeroDev session-key settlement adapter for Moesi. Implements the abstract Settlement interface over @zerodev/sdk + @zerodev/permissions.
One signature per action-bearing chain enables a scope-locked session key that fires each deploy and post-deploy as an ERC-4337 UserOperation.
Install
npm install @moesi/settle-zerodev viemUsage
import {
prepareKernels,
authorize,
executeAction,
zerodevSettlement,
} from "@moesi/settle-zerodev";
// 1. Predict the same kernel address on every chain
const prepared = await prepareKernels(ownerSigner, chains);
const kernelAddresses = new Map(
[...prepared].map(([k, v]) => [k, v.address] as const),
);
// 2. Resolve the plan (from @moesi/resolver)
const plan = await resolvePlan(manifest, chains, kernelAddresses, config);
// 3. One signature per action-bearing chain → CallPolicy-locked session key
const auth = await authorize(
ownerSigner,
prepared,
chains,
plan.actions,
plan.chainScope,
console.log,
);
// 4. Fire every action silently under the session key
for (const [index, action] of plan.actions.entries()) {
const authorized = auth.find((item) => item.chainKey === action.chainKey);
const chain = chains.find((item) => item.key === action.chainKey);
if (!authorized || !chain?.zerodevRpc) throw new Error("Missing authorized chain");
const result = await executeAction(
authorized,
chain.zerodevRpc,
action,
index,
console.log,
);
if (!result.ok) throw result.error;
console.log(result.txHash);
}Kernel version pinning
The adapter supports Kernel 0.3.1, 0.3.2, and 0.3.3; 0.3.3 remains the
default. Kernel version participates in account address derivation, so existing
fleets must pass the version they were created with everywhere it is accepted:
const kernelVersion = "0.3.2" as const;
const prepared = await prepareKernels(ownerSigner, chains, kernelVersion);
const addresses = await predictKernelAddresses(ownerSigner, chains, kernelVersion);
const revoked = await revokeGrant(
ownerSigner,
chain,
revocation,
actionIndex,
onEvent,
kernelVersion,
);PreparedKernel and authorized-chain values carry the selected version so
execution deserializes against the matching account.
Policy-native authorization
authorizePolicies accepts provider-neutral exact-action and
erc20-transfer artifacts from @moesi/settlement. Here, exact-action
identifies the reviewed action; the per-constraint result—not the name—states
which parts are enforced on-chain. The adapter compiles target, selector,
supported argument rules, value, expiry, and configured transaction/rate limits
into Kernel policies. The EOA compiler fails closed when a policy requires
on-chain enforcement.
Ordinary CallPolicy sessions still report full calldata hashing as advisory.
For exact bytes on-chain, use kernel-threshold-2-of-2 and
compileKernelThresholdExactAction: the authority approval binds Kernel
sender/callData/nonce and the CI signature binds the complete final
UserOperation. encodeKernelThresholdSignatures emits the patched validator's
required approval-then-UserOperation wire order. Either signature alone, a
stale context, or a one-byte callData mutation fails on-chain.
The older authorize(..., chainScope, ...) API remains as a compatibility
surface. New funding-pool and apply flows use policy-native authorization.
compileZeroDevPolicies supports recipientMatch: "one-of" | "equal", an
explicit any-recipient mode (recipients: []), and
rateLimit.variant: "default" | "with-reset". These switches exist for
byte-for-byte permission-ID parity with already installed grants; inspect the
compiled constraint evidence rather than inferring enforcement from the policy
name.
Owner administration and activity
createOwnerKernelClient builds an owner-sudo client over caller-provided
bundler and optional paymaster transports. Use it for owner administration or
revocation when credentials stay behind an application proxy; it is not a
bounded session-key grant. The same Kernel version pin and account index must
match the existing account.
reconstructActivity scans confirmed block ranges and reconstructs ERC-20 and
native outflows, attributing permission-validated UserOperations through their
nonce-encoded permission ID. Pair it with findDeployBlock from
@moesi/resolver; a null deploy block is inconclusive when archive history
is unavailable, so applications need a visible lookback fallback.
Bundler errors
Owner-signed clients automatically convert failures from
sendUserOperation and waitForUserOperationReceipt into structured
MoesiError instances. For custom UserOperation flows, use the same public
decoder directly:
import { explainBundlerError } from "@moesi/settle-zerodev";
try {
await client.sendUserOperation(request);
} catch (error) {
const { kind, problem, cause, fix } = explainBundlerError(error);
console.error({ kind, problem, cause, fix });
}Pass { target, data } as the optional second argument when inner-call
context is available. AA_ERROR_TABLE and flattenErrorMessages are also
public for consumers that need custom presentation.
Custom-chain settlement
Custom entries may supply a BYO ERC-4337 bundler and an independent optional
paymaster. A funded Kernel self-pays when no paymaster exists. Tier B can submit
an already signed UserOperation directly through EntryPoint handleOps, so the
operator EOA fronts gas without receiving policy authority.
getReferenceKernelBootstrapManifest() and bootstrapKernelStack() implement
the Tier-C virgin-chain path. They deploy and byte-attest Nick's deterministic
deployment proxy, CreateX, EntryPoint 0.7/SenderCreator, Kernel 0.3.3 factory and
implementation, the patched WeightedValidator, and the permission policies.
The preflight examines every canonical address before the first transaction;
an unexpected predecessor fails closed, while exact predecessors make the
operation restart-safe. EIP-712 runtime hashes are derived for the target chain
ID rather than copied from Ethereum.
License
Apache-2.0
