@sm-lab/recipes 0.2.0 → 0.3.0
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/dist/cli.mjs +201 -3
- package/dist/cli.mjs.map +1 -1
- package/dist/cm.d.mts +2 -2
- package/dist/cm.mjs +1 -1
- package/dist/cm.mjs.map +1 -1
- package/dist/{context-BiYdAnKn.d.mts → context-7_EMmCZw.d.mts} +6 -6
- package/dist/{context-BiYdAnKn.d.mts.map → context-7_EMmCZw.d.mts.map} +1 -1
- package/dist/{cl-activate-CPqr6v_D.mjs → exit-request-c0CS5wKu.mjs} +417 -9
- package/dist/exit-request-c0CS5wKu.mjs.map +1 -0
- package/dist/index.d.mts +178 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3 -3
- package/dist/{topup-B2NzJV7_.mjs → topup-CuqmB14u.mjs} +117 -11
- package/dist/topup-CuqmB14u.mjs.map +1 -0
- package/package.json +3 -3
- package/dist/cl-activate-CPqr6v_D.mjs.map +0 -1
- package/dist/topup-B2NzJV7_.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"topup-CuqmB14u.mjs","names":["DEFAULTS"],"sources":["../src/client.ts","../src/context.ts","../src/act-as.ts","../src/random.ts","../src/keys.ts","../src/recipes/add-keys.ts","../src/roles.ts","../src/recipes/deposit.ts","../src/recipes/reads.ts","../src/recipes/topup.ts"],"sourcesContent":["import { createTestClient, http, publicActions, walletActions } from 'viem';\n\n/**\n * The single viem client recipes use. An anvil test client (for setBalance /\n * impersonateAccount / increaseTime / snapshot) extended with public actions\n * (readContract / simulateContract / getChainId) and wallet actions (writeContract).\n * No `chain` is set — anvil forks vary by chainId, so writes pass `chain: null`.\n */\nexport function makeClient(rpcUrl: string) {\n return createTestClient({ mode: 'anvil', transport: http(rpcUrl) })\n .extend(publicActions)\n .extend(walletActions);\n}\n\n/** Structural type recipes depend on — satisfied by makeClient's return or a test fake. */\nexport type RecipeClient = ReturnType<typeof makeClient>;\n","import {\n accountingAbi,\n addresses as DEFAULTS,\n csModuleAbi,\n curatedGateAbi,\n lidoLocatorAbi,\n vettedGateAbi,\n} from '@sm-lab/receipts';\nimport type {\n AddressBook,\n ChainName,\n CmAddressBook,\n CsmAddressBook,\n Hex,\n ModuleName,\n} from '@sm-lab/receipts';\nimport { makeClient, type RecipeClient } from './client';\n\n/** Module-suite snapshot + the protocol addresses resolved on-chain by connect(). */\nexport type ResolvedAddresses = AddressBook & {\n stakingRouter: Hex;\n vebo: Hex;\n lido: Hex;\n withdrawalQueue: Hex;\n burner: Hex;\n};\n\nexport interface Ctx {\n client: RecipeClient;\n module: ModuleName;\n addresses: ResolvedAddresses;\n clMockUrl?: string;\n}\n\nexport interface ConnectOptions {\n module: ModuleName;\n /** Required unless `client` is injected. */\n rpcUrl?: string;\n /** Inject a prebuilt client (tests, or a shared client). */\n client?: RecipeClient;\n /** Override the module-suite snapshot; defaults to @sm-lab/receipts by chainId. */\n addresses?: AddressBook;\n clMockUrl?: string;\n}\n\nexport type CsmGateSelector = 'ics' | 'idvtc';\nexport type CmGateSelector = 'po' | 'pto' | 'pgo' | 'do' | 'eeo' | 'iodc' | 'iodcp';\nexport type GateSelector = CsmGateSelector | CmGateSelector;\n\nconst CM_SELECTORS: Record<string, number> = {\n po: 0,\n pto: 1,\n pgo: 2,\n do: 3,\n eeo: 4,\n iodc: 5,\n iodcp: 6,\n};\n\n// Curated gate address-book keys, in selector/index order (po..iodcp).\nconst CM_GATE_KEYS = [\n 'CuratedGatePO',\n 'CuratedGatePTO',\n 'CuratedGatePGO',\n 'CuratedGateDO',\n 'CuratedGateEEO',\n 'CuratedGateIODC',\n 'CuratedGateIODCP',\n] as const satisfies readonly (keyof CmAddressBook)[];\n\nfunction defaultSnapshot(chainId: number, module: ModuleName): AddressBook {\n for (const chainKey of Object.keys(DEFAULTS) as ChainName[]) {\n const byModule = DEFAULTS[chainKey] as Partial<Record<ModuleName, AddressBook>>;\n const book = byModule[module];\n if (book && (book as { ChainId: number }).ChainId === chainId) return book;\n }\n throw new Error(\n `@sm-lab/recipes: no default snapshot for chainId=${chainId}, module=${module} — pass addresses explicitly`,\n );\n}\n\n/**\n * The only place chain / addresses / module resolve. Prefers the baked `protocol` block\n * from the address book (zero on-chain reads) and falls back to reading the five protocol\n * addresses from LidoLocator when the block is absent.\n */\nexport async function connect(opts: ConnectOptions): Promise<Ctx> {\n const client = opts.client ?? makeClient(requireRpcUrl(opts));\n const chainId = await client.getChainId();\n const book = opts.addresses ?? defaultSnapshot(chainId, opts.module);\n\n const protocol = book.protocol\n ? {\n stakingRouter: book.protocol.stakingRouter,\n vebo: book.protocol.validatorsExitBusOracle,\n lido: book.protocol.lido,\n withdrawalQueue: book.protocol.withdrawalQueue,\n burner: book.protocol.burner,\n }\n : await resolveProtocolFromLocator(client, book.LidoLocator as Hex);\n\n return {\n client,\n module: opts.module,\n clMockUrl: opts.clMockUrl,\n addresses: { ...book, ...protocol } as ResolvedAddresses,\n };\n}\n\nasync function resolveProtocolFromLocator(\n client: RecipeClient,\n locator: Hex,\n): Promise<{ stakingRouter: Hex; vebo: Hex; lido: Hex; withdrawalQueue: Hex; burner: Hex }> {\n const loc = { address: locator, abi: lidoLocatorAbi } as const;\n const [stakingRouter, vebo, lido, withdrawalQueue, burner] = await Promise.all([\n client.readContract({ ...loc, functionName: 'stakingRouter' }),\n client.readContract({ ...loc, functionName: 'validatorsExitBusOracle' }),\n client.readContract({ ...loc, functionName: 'lido' }),\n client.readContract({ ...loc, functionName: 'withdrawalQueue' }),\n client.readContract({ ...loc, functionName: 'burner' }),\n ]);\n return { stakingRouter, vebo, lido, withdrawalQueue, burner };\n}\n\nfunction requireRpcUrl(opts: ConnectOptions): string {\n if (!opts.rpcUrl)\n throw new Error('@sm-lab/recipes: connect() needs rpcUrl (or an injected client)');\n return opts.rpcUrl;\n}\n\nconst STATIC = {\n Accounting: accountingAbi,\n VettedGate: vettedGateAbi,\n CuratedGate: curatedGateAbi,\n LidoLocator: lidoLocatorAbi,\n} as const;\ntype StaticName = keyof typeof STATIC;\n\nexport function contract(ctx: Ctx, name: 'module'): { address: Hex; abi: typeof csModuleAbi };\nexport function contract<N extends StaticName>(\n ctx: Ctx,\n name: N,\n): { address: Hex; abi: (typeof STATIC)[N] };\nexport function contract(ctx: Ctx, name: StaticName | 'module') {\n if (name === 'module') {\n // csModuleAbi anchors the shared IBaseModule surface (getNodeOperator,\n // addValidatorKeysETH, …) for BOTH modules — those fragments are byte-identical\n // across CSModule/CuratedModule, so selectors and decoding match. Only the ADDRESS\n // switches by ctx.module.\n const address = (\n ctx.module === 'cm'\n ? (ctx.addresses as CmAddressBook).CuratedModule\n : (ctx.addresses as CsmAddressBook).CSModule\n ) as Hex;\n return { address, abi: csModuleAbi };\n }\n return { address: (ctx.addresses as unknown as Record<string, Hex>)[name], abi: STATIC[name] };\n}\n\n/**\n * Resolve a gate selector to an address (the `_resolve-gate-addr` port). Accepted forms:\n * a raw `0x…` 40-hex address (any module); for csm — `ics` → IcsGate, `idvtc` →\n * IdvtcGate (v3-only; throws on pre-v3 snapshots lacking it);\n * for cm — `po|pto|pgo|do|eeo|iodc|iodcp` or a numeric index → the named curated gates.\n */\nexport function resolveGate(ctx: Ctx, selector: string): Hex {\n if (/^0x[0-9a-fA-F]{40}$/.test(selector)) return selector as Hex;\n if (ctx.module === 'cm') {\n const idx = CM_SELECTORS[selector] ?? (/^\\d+$/.test(selector) ? Number(selector) : undefined);\n if (idx === undefined)\n throw new Error(`@sm-lab/recipes: unknown cm gate selector \"${selector}\"`);\n const key = CM_GATE_KEYS[idx];\n if (!key) throw new Error(`@sm-lab/recipes: cm gate index ${idx} out of range`);\n return (ctx.addresses as CmAddressBook)[key];\n }\n if (selector === 'ics') return (ctx.addresses as CsmAddressBook).IcsGate;\n if (selector === 'idvtc') {\n const g = (ctx.addresses as CsmAddressBook).IdvtcGate;\n if (!g)\n throw new Error('@sm-lab/recipes: idvtc gate not in this snapshot (v3-only; absent pre-v3)');\n return g;\n }\n throw new Error(`@sm-lab/recipes: unknown csm gate selector \"${selector}\"`);\n}\n","import { parseEther } from 'viem';\nimport type { Abi } from 'viem';\nimport type { Hex } from '@sm-lab/receipts';\nimport type { Ctx } from './context';\n\n/**\n * Run `fn` as a privileged account `who` on the fork. Funds it (anvil_setBalance),\n * unlocks it (anvil_impersonateAccount), runs the body, and always stops impersonating.\n * Replaces every Solidity `broadcast*` modifier. `who` is a RAW address — pass a contract\n * address (e.g. the module, verifier, stakingRouter) or a role member resolved via roleMember.\n */\nexport async function actAs<T>(ctx: Ctx, who: Hex, fn: (from: Hex) => Promise<T>): Promise<T> {\n await ctx.client.setBalance({ address: who, value: parseEther('100') });\n await ctx.client.impersonateAccount({ address: who });\n try {\n return await fn(who);\n } finally {\n await ctx.client.stopImpersonatingAccount({ address: who });\n }\n}\n\n/** Read `getRoleMember(role, 0)` — the canonical admin/governance member of an AccessControl role. */\nexport async function roleMember(\n ctx: Ctx,\n target: { address: Hex; abi: Abi },\n role: Hex,\n): Promise<Hex> {\n const member = await ctx.client.readContract({\n address: target.address,\n abi: target.abi,\n functionName: 'getRoleMember',\n args: [role, 0n],\n });\n return member as Hex;\n}\n","import { toHex } from 'viem';\nimport type { Hex } from '@sm-lab/receipts';\n\n/**\n * A 32-byte cryptographically-random seed, hex-encoded. The shared origin for every recipe that\n * needs fresh-but-reproducible-on-demand randomness (key material, reward draws, operator\n * addresses) — pass a fixed seed for deterministic output, call this to mint a fresh one.\n */\nexport function randomSeed(): Hex {\n const bytes = new Uint8Array(32);\n globalThis.crypto.getRandomValues(bytes);\n return toHex(bytes);\n}\n","import { makeDepositKeys } from '@sm-lab/keys';\nimport { concat, keccak256, toBytes } from 'viem';\nimport type { Hex } from '@sm-lab/receipts';\nimport { randomSeed } from './random';\n\n/**\n * Fixed BIP-39 mnemonic for deterministic test/recipe key derivation. NOT a production\n * key — exists only for hermetic reproducibility. The seed controls the startIndex,\n * which determines the EIP-2334 derivation path slice used.\n */\nconst RECIPE_MNEMONIC =\n 'impact exit example acquire drastic cement usage float mesh source private bulb twenty guitar neglect';\n\n/**\n * Deterministic real BLS validator keys derived from a fixed mnemonic at a seed-derived\n * startIndex. Produces genuine 48-byte G1 pubkeys + 96-byte G2 BLS signatures — NOT\n * keccak-expanded pseudo-keys. Pass a fixed `seed` for reproducible tests.\n *\n * The seed maps to a `startIndex` via `keccak256(seed) % 2^20` so different seeds yield\n * non-overlapping key ranges (1M range >> any realistic `count`).\n */\nexport async function randomKeys(\n count: number,\n seed?: Hex,\n): Promise<{ publicKeys: Hex[]; signatures: Hex[]; packedKeys: Hex; packedSignatures: Hex }> {\n const root = seed ?? randomSeed();\n const startIndex = Number(BigInt(keccak256(toBytes(root))) % 2n ** 20n);\n\n const { keys } = await makeDepositKeys({\n mnemonic: RECIPE_MNEMONIC,\n count,\n startIndex,\n // NOTE: withdrawal_credentials + fork-version DO sign into the BLS signature (the sig covers\n // a DepositMessage hash under a domain derived from the fork version). But this path never\n // verifies it: CSModule.addValidatorKeysETH → SigningKeys.saveKeysSigs only checks length +\n // non-empty (no BLS verify), and the deposit recipe uses obtainDepositData (StakingRouter-\n // impersonated), not the beacon DepositContract. So the chosen chain is arbitrary-but-valid here.\n chain: 'hoodi',\n });\n\n const publicKeys = keys.map((k) => k.pubkey as Hex);\n const signatures = keys.map((k) => k.signature as Hex);\n\n return {\n publicKeys,\n signatures,\n packedKeys: count === 0 ? '0x' : (concat(publicKeys) as Hex),\n packedSignatures: count === 0 ? '0x' : (concat(signatures) as Hex),\n };\n}\n","import type { Hex } from '@sm-lab/receipts';\nimport { actAs } from '../act-as';\nimport { contract, type Ctx } from '../context';\nimport { randomKeys } from '../keys';\n\nexport interface AddKeysOptions {\n noId: bigint;\n count: number;\n /** Injectable seed for reproducible keys. */\n seed?: Hex;\n}\n\nexport interface AddKeysResult {\n publicKeys: Hex[];\n}\n\n/**\n * Add `count` fresh validator keys to operator `noId`, paying the required bond, as the\n * operator's manager. Returns the generated pubkeys. (Port of `NodeOperators.addKeys`.)\n */\nexport async function addKeys(ctx: Ctx, opts: AddKeysOptions): Promise<AddKeysResult> {\n const m = contract(ctx, 'module');\n const acc = contract(ctx, 'Accounting');\n\n const op = await ctx.client.readContract({\n ...m,\n functionName: 'getNodeOperator',\n args: [opts.noId],\n });\n const manager = (op as { managerAddress: Hex }).managerAddress;\n\n const value = await ctx.client.readContract({\n ...acc,\n functionName: 'getRequiredBondForNextKeys',\n args: [opts.noId, BigInt(opts.count)],\n });\n\n const { publicKeys, packedKeys, packedSignatures } = await randomKeys(opts.count, opts.seed);\n\n await actAs(ctx, manager, (from) =>\n ctx.client.writeContract({\n ...m,\n functionName: 'addValidatorKeysETH',\n args: [manager, opts.noId, BigInt(opts.count), packedKeys, packedSignatures],\n account: from,\n value: value as bigint,\n chain: null,\n }),\n );\n\n return { publicKeys };\n}\n","import { keccak256, toBytes } from 'viem';\nimport type { Hex } from '@sm-lab/receipts';\n\n/** OZ AccessControl default admin role (all-zero bytes32). */\nexport const DEFAULT_ADMIN_ROLE = `0x${'0'.repeat(64)}` as Hex;\n/** MerkleGate: keccak256(\"SET_TREE_ROLE\"). */\nexport const SET_TREE_ROLE = keccak256(toBytes('SET_TREE_ROLE'));\n/** PausableWithRoles: keccak256(\"RESUME_ROLE\"). */\nexport const RESUME_ROLE = keccak256(toBytes('RESUME_ROLE'));\n/** PausableWithRoles: keccak256(\"PAUSE_ROLE\"). */\nexport const PAUSE_ROLE = keccak256(toBytes('PAUSE_ROLE'));\n/** BaseModule: keccak256(\"REPORT_GENERAL_DELAYED_PENALTY_ROLE\"). */\nexport const REPORT_GENERAL_DELAYED_PENALTY_ROLE = keccak256(\n toBytes('REPORT_GENERAL_DELAYED_PENALTY_ROLE'),\n);\n/** BaseModule: keccak256(\"SETTLE_GENERAL_DELAYED_PENALTY_ROLE\"). */\nexport const SETTLE_GENERAL_DELAYED_PENALTY_ROLE = keccak256(\n toBytes('SETTLE_GENERAL_DELAYED_PENALTY_ROLE'),\n);\n","import { maxUint256, size } from 'viem';\nimport { actAs } from '../act-as';\nimport { contract, type Ctx } from '../context';\n\n/**\n * Deposit up to `count` of an operator's depositable keys (StakingRouter-gated). Flushes pending\n * deposit-info, caps to the module's depositable count, and returns the number actually deposited\n * (derived from the keys `obtainDepositData` hands back). Throws if a positive request finds nothing\n * depositable — the Solidity helper silently no-ops there. (Port of `NodeOperators.deposit`.)\n */\nexport async function deposit(\n ctx: Ctx,\n opts: { count: number | bigint },\n): Promise<{ deposited: bigint }> {\n const m = contract(ctx, 'module');\n const requested = BigInt(opts.count);\n\n return actAs(ctx, ctx.addresses.stakingRouter, async (from) => {\n await ctx.client.writeContract({\n ...m,\n functionName: 'batchDepositInfoUpdate',\n args: [maxUint256],\n account: from,\n chain: null,\n });\n\n const summary = await ctx.client.readContract({\n ...m,\n functionName: 'getStakingModuleSummary',\n });\n const [, , depositable] = summary;\n const capped = requested < depositable ? requested : depositable;\n if (requested > 0n && capped === 0n) {\n throw new Error('@sm-lab/recipes: deposit found nothing depositable for this module');\n }\n\n const { result, request } = await ctx.client.simulateContract({\n ...m,\n functionName: 'obtainDepositData',\n args: [capped, '0x'],\n account: from,\n });\n await ctx.client.writeContract({ ...request, chain: null });\n\n const [pubkeys] = result;\n return { deposited: BigInt(size(pubkeys) / 48) };\n });\n}\n","import { size } from 'viem';\nimport { curatedGateAbi, vettedGateAbi } from '@sm-lab/receipts';\nimport type { Hex } from '@sm-lab/receipts';\nimport { contract, resolveGate, type Ctx } from '../context';\n\n/** One key's 48-byte pubkey from on-chain storage. Throws if no key exists at `keyIndex`. */\nexport async function getPubkey(ctx: Ctx, opts: { noId: bigint; keyIndex: bigint }): Promise<Hex> {\n const m = contract(ctx, 'module');\n const keys = (await ctx.client.readContract({\n ...m,\n functionName: 'getSigningKeys',\n args: [opts.noId, opts.keyIndex, 1n],\n })) as Hex;\n // count=1 → a single packed 48-byte pubkey; no per-48 slice needed. Guard `undefined`\n // (unscripted fake reads) before `size()` so it throws the clean error, not a viem internal.\n if (!keys || size(keys) !== 48) {\n throw new Error(\n `@sm-lab/recipes: no key found for operator ${opts.noId} at index ${opts.keyIndex}`,\n );\n }\n return keys;\n}\n\n/** Allocated balance (wei) for one key. */\nexport async function getKeyBalance(\n ctx: Ctx,\n opts: { noId: bigint; keyIndex: bigint },\n): Promise<bigint> {\n const m = contract(ctx, 'module');\n const balances = (await ctx.client.readContract({\n ...m,\n functionName: 'getKeyAllocatedBalances',\n args: [opts.noId, opts.keyIndex, 1n],\n })) as readonly bigint[];\n const wei = balances[0]; // noUncheckedIndexedAccess: guard\n if (wei === undefined) {\n throw new Error(\n `@sm-lab/recipes: no allocated balance for operator ${opts.noId} at index ${opts.keyIndex}`,\n );\n }\n return wei;\n}\n\nexport interface BondCurveInterval {\n minKeysCount: bigint;\n minBond: bigint;\n trend: bigint;\n}\nexport interface BondCurveInfo {\n intervals: BondCurveInterval[];\n}\n\n/** Read a bond curve by id from Accounting (read-only). */\nexport async function getCurveInfo(ctx: Ctx, opts: { curveId: bigint }): Promise<BondCurveInfo> {\n const acc = contract(ctx, 'Accounting');\n const info = (await ctx.client.readContract({\n ...acc,\n functionName: 'getCurveInfo',\n args: [opts.curveId],\n })) as BondCurveInfo;\n return info;\n}\n\nexport interface BondInfo {\n currentBond: bigint;\n requiredBond: bigint;\n lockedBond: bigint;\n bondDebt: bigint;\n pendingSharesToSplit: bigint;\n}\n\n/** Read an operator's bond summary from Accounting (read-only). */\nexport async function bondInfo(ctx: Ctx, opts: { noId: bigint }): Promise<BondInfo> {\n const acc = contract(ctx, 'Accounting');\n return (await ctx.client.readContract({\n ...acc,\n functionName: 'getNodeOperatorBondInfo',\n args: [opts.noId],\n })) as BondInfo;\n}\n\n/** All of an operator's pubkeys (48 bytes each), in index order (read-only). */\nexport async function operatorKeys(ctx: Ctx, opts: { noId: bigint }): Promise<Hex[]> {\n const m = contract(ctx, 'module');\n const op = await ctx.client.readContract({\n ...m,\n functionName: 'getNodeOperator',\n args: [opts.noId],\n });\n const total = (op as { totalAddedKeys: number }).totalAddedKeys;\n if (total === 0) return [];\n const packed = (await ctx.client.readContract({\n ...m,\n functionName: 'getSigningKeys',\n args: [opts.noId, 0n, BigInt(total)],\n })) as Hex;\n const hex = packed.slice(2); // drop 0x; 48 bytes = 96 hex chars per key\n const keys: Hex[] = [];\n for (let i = 0; i < total; i++) keys.push(`0x${hex.slice(i * 96, (i + 1) * 96)}` as Hex);\n return keys;\n}\n\n/** All of an operator's deposited-key allocated balances (wei), in index order (read-only). */\nexport async function keyBalances(ctx: Ctx, opts: { noId: bigint }): Promise<bigint[]> {\n const m = contract(ctx, 'module');\n const op = await ctx.client.readContract({\n ...m,\n functionName: 'getNodeOperator',\n args: [opts.noId],\n });\n const total = (op as { totalDepositedKeys: number }).totalDepositedKeys;\n if (total === 0) return [];\n const balances = (await ctx.client.readContract({\n ...m,\n functionName: 'getKeyAllocatedBalances',\n args: [opts.noId, 0n, BigInt(total)],\n })) as readonly bigint[];\n return [...balances];\n}\n\n/** Total number of node operators in the module (read-only). */\nexport async function operatorsCount(ctx: Ctx): Promise<bigint> {\n const m = contract(ctx, 'module');\n return (await ctx.client.readContract({ ...m, functionName: 'getNodeOperatorsCount' })) as bigint;\n}\n\n/** The highest node operator id (count - 1). Throws when there are no operators. */\nexport async function getLastOperator(ctx: Ctx): Promise<bigint> {\n const count = await operatorsCount(ctx);\n if (count === 0n) throw new Error('@sm-lab/recipes: no node operators');\n return count - 1n;\n}\n\nexport interface GateTree {\n selector: string;\n address: Hex;\n treeRoot: Hex;\n treeCid: string;\n}\n\n/** Read a gate's current merkle tree params (root + cid) by selector (read-only). */\nexport async function getGateTree(ctx: Ctx, opts: { selector: string }): Promise<GateTree> {\n const address = resolveGate(ctx, opts.selector);\n const abi = ctx.module === 'cm' ? curatedGateAbi : vettedGateAbi;\n const gate = { address, abi } as const;\n const treeRoot = (await ctx.client.readContract({ ...gate, functionName: 'treeRoot' })) as Hex;\n const treeCid = (await ctx.client.readContract({ ...gate, functionName: 'treeCid' })) as string;\n return { selector: opts.selector, address, treeRoot, treeCid };\n}\n","import type { Hex } from '@sm-lab/receipts';\nimport { actAs } from '../act-as';\nimport { contract, type Ctx } from '../context';\nimport { getPubkey } from './reads';\n\n/** Per-key top-up cap (2016 ether). Matches `NodeOperators.MAX_TOPUP_PER_KEY`. */\nconst MAX_TOPUP_PER_KEY = 2016n * 10n ** 18n;\n\n/**\n * Top up the allocated balance of a single deposited key (StakingRouter-gated). Validates the key\n * exists and is not withdrawn, then writes a one-element `allocateDeposits` for it. Port of\n * `NodeOperators.increaseAllocatedBalance` — returns the `amountWei` it allocated.\n */\nexport async function increaseAllocatedBalance(\n ctx: Ctx,\n opts: { noId: bigint; keyIndex: bigint; amountWei: bigint },\n): Promise<{ amountWei: bigint }> {\n const m = contract(ctx, 'module');\n const { noId, keyIndex, amountWei } = opts;\n\n // Validate in the source's order — bounds, then withdrawn, THEN the pubkey read. The two checks\n // gate the pubkey lookup deliberately: on a real fork `getSigningKeys` reverts on an out-of-range\n // index, so reading it before the bounds check (e.g. batched in a parallel read) surfaces an\n // opaque revert instead of these precise errors (mirrors NodeOperators.s.sol:311-315).\n const op = await ctx.client.readContract({ ...m, functionName: 'getNodeOperator', args: [noId] });\n const total = (op as { totalDepositedKeys: number }).totalDepositedKeys;\n if (keyIndex >= BigInt(total)) {\n throw new Error(\n `@sm-lab/recipes: key index ${keyIndex} out of bounds (operator ${noId} has ${total} deposited keys)`,\n );\n }\n const withdrawn = await ctx.client.readContract({\n ...m,\n functionName: 'isValidatorWithdrawn',\n args: [noId, keyIndex],\n });\n if (withdrawn) {\n throw new Error(`@sm-lab/recipes: key ${keyIndex} of operator ${noId} is withdrawn`);\n }\n // count=1 → a single packed 48-byte pubkey; reuse reads.ts (same read + 48-byte guard + error).\n const key = await getPubkey(ctx, { noId, keyIndex });\n\n await actAs(ctx, ctx.addresses.stakingRouter, (from) =>\n ctx.client.writeContract({\n ...m,\n functionName: 'allocateDeposits',\n args: [amountWei, [key], [keyIndex], [noId], [amountWei]],\n account: from,\n chain: null,\n }),\n );\n\n return { amountWei };\n}\n\n/**\n * Top up every not-yet-allocated, not-withdrawn deposited key of an operator, one at a time in\n * strict ascending key-index order — `TopUpQueueOps` enforces a global FIFO queue head, so the\n * writes must be sequential and ordered (capped at `MAX_TOPUP_PER_KEY` per key). Port of\n * `NodeOperators.topUpActiveKeys` — returns the count it topped up.\n */\nexport async function topUpActiveKeys(\n ctx: Ctx,\n opts: { noId: bigint },\n): Promise<{ toppedUp: number }> {\n const m = contract(ctx, 'module');\n const { noId } = opts;\n\n const op = await ctx.client.readContract({ ...m, functionName: 'getNodeOperator', args: [noId] });\n const total = (op as { totalDepositedKeys: number }).totalDepositedKeys;\n if (total === 0) {\n throw new Error(`@sm-lab/recipes: operator ${noId} has no deposited keys`);\n }\n\n const allocated = (await ctx.client.readContract({\n ...m,\n functionName: 'getKeyAllocatedBalances',\n args: [noId, 0n, BigInt(total)],\n })) as readonly bigint[];\n // Guard the read against noUncheckedIndexedAccess: a too-short array would otherwise make\n // `allocated[i] === 0n` silently false-y for the missing tail.\n if (allocated.length !== total) {\n throw new Error(\n `@sm-lab/recipes: getKeyAllocatedBalances returned ${allocated.length} entries (expected ${total})`,\n );\n }\n\n // Reads are pulled up-front in parallel for every key — a harmless divergence from the source's\n // lazy per-key short-circuit (the extra `isValidatorWithdrawn`/`getSigningKeys` reads are free on\n // a fork and order-independent).\n const keys = await Promise.all(\n Array.from({ length: total }, (_, i) => i).map(async (i) => ({\n i,\n pubkey: (await ctx.client.readContract({\n ...m,\n functionName: 'getSigningKeys',\n args: [noId, BigInt(i), 1n],\n })) as Hex,\n withdrawn: (await ctx.client.readContract({\n ...m,\n functionName: 'isValidatorWithdrawn',\n args: [noId, BigInt(i)],\n })) as boolean,\n })),\n );\n\n // Skip already-allocated (`allocated[i] !== 0`) and withdrawn keys — the source's two `continue`s.\n const workList = keys.filter(({ i, withdrawn }) => allocated[i] === 0n && !withdrawn);\n\n // No-op without entering impersonation when nothing needs topping up (matches the\n // createOperatorGroup \"no chain call on no-op\" discipline).\n if (workList.length === 0) {\n return { toppedUp: 0 };\n }\n\n await actAs(ctx, ctx.addresses.stakingRouter, async (from) => {\n for (const { i, pubkey } of workList) {\n // eslint-disable-next-line no-await-in-loop -- sequential by necessity (TopUpQueueOps FIFO queue head; impersonation is global state)\n await ctx.client.writeContract({\n ...m,\n functionName: 'allocateDeposits',\n args: [MAX_TOPUP_PER_KEY, [pubkey], [BigInt(i)], [noId], [MAX_TOPUP_PER_KEY]],\n account: from,\n chain: null,\n });\n }\n });\n\n return { toppedUp: workList.length };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sm-lab/recipes",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "TypeScript recipes to prepare Lido SM on-chain state on an anvil fork (rewritten fork.just; no Foundry)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
"commander": "^15.0.0",
|
|
41
41
|
"dotenv": "^17.4.2",
|
|
42
42
|
"viem": "~2.53.1",
|
|
43
|
+
"@sm-lab/keys": "0.2.1",
|
|
43
44
|
"@sm-lab/merkle": "1.2.0",
|
|
44
|
-
"@sm-lab/
|
|
45
|
-
"@sm-lab/receipts": "0.1.0"
|
|
45
|
+
"@sm-lab/receipts": "0.2.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"tsdown": "^0.22.3",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cl-activate-CPqr6v_D.mjs","names":["makeRewards","makeRewardsTree"],"sources":["../src/recipes/operator-info.ts","../src/recipes/chain.ts","../src/recipes/address-changes.ts","../src/encode.ts","../src/recipes/vetting.ts","../src/recipes/validators.ts","../src/recipes/penalties.ts","../src/recipes/bond.ts","../src/recipes/set-gate.ts","../src/recipes/rewards.ts","../src/cl-mock.ts","../src/recipes/cl-activate.ts"],"sourcesContent":["import type { Hex } from '@sm-lab/receipts';\nimport { contract, type Ctx } from '../context';\n\n/** Decoded NodeOperator (abitype: uint32/uint8 → number, address → Hex, bool → boolean). */\nexport interface OperatorInfo {\n totalAddedKeys: number;\n totalWithdrawnKeys: number;\n totalDepositedKeys: number;\n totalVettedKeys: number;\n stuckValidatorsCount: number;\n depositableValidatorsCount: number;\n targetLimit: number;\n targetLimitMode: number;\n totalExitedKeys: number;\n enqueuedCount: number;\n managerAddress: Hex;\n proposedManagerAddress: Hex;\n rewardAddress: Hex;\n proposedRewardAddress: Hex;\n extendedManagerPermissions: boolean;\n usedPriorityQueue: boolean;\n}\n\n/** Typed read of a node operator's on-chain record. (Port of `NodeOperators.operatorInfo`.) */\nexport async function operatorInfo(ctx: Ctx, opts: { noId: bigint }): Promise<OperatorInfo> {\n const m = contract(ctx, 'module');\n const op = await ctx.client.readContract({\n ...m,\n functionName: 'getNodeOperator',\n args: [opts.noId],\n });\n return op as OperatorInfo;\n}\n","import type { Hex } from '@sm-lab/receipts';\nimport type { Ctx } from '../context';\n\n/** Advance fork time by `seconds` and mine a block (evm_increaseTime + evm_mine). */\nexport async function warpBy(ctx: Ctx, seconds: number | bigint): Promise<void> {\n await ctx.client.increaseTime({ seconds: Number(seconds) });\n await ctx.client.mine({ blocks: 1 });\n}\n\n/**\n * Warp fork time to an absolute unix timestamp and mine a block\n * (evm_setNextBlockTimestamp + evm_mine). The absolute counterpart of `warpBy` — used by\n * `submitRewards`'s consensus-frame wait. Over RPC, `setNextBlockTimestamp` + `mine` reproduces\n * Foundry's `vm.warp(ts)` (the post-warp `block.timestamp` settles automatically after mining).\n */\nexport async function warpTo(ctx: Ctx, timestamp: number | bigint): Promise<void> {\n await ctx.client.setNextBlockTimestamp({ timestamp: BigInt(timestamp) });\n await ctx.client.mine({ blocks: 1 });\n}\n\n/** Take an anvil state snapshot; returns the snapshot id (evm_snapshot). */\nexport function snapshot(ctx: Ctx): Promise<Hex> {\n return ctx.client.snapshot();\n}\n\n/** Revert the fork to a snapshot id (evm_revert). */\nexport async function revert(ctx: Ctx, id: Hex): Promise<void> {\n await ctx.client.revert({ id });\n}\n","import type { Hex } from '@sm-lab/receipts';\nimport { actAs } from '../act-as';\nimport { contract, type Ctx } from '../context';\n\n/** Propose a new manager address for an operator (signed by the current manager). */\nexport async function proposeManager(\n ctx: Ctx,\n opts: { noId: bigint; proposed: Hex },\n): Promise<void> {\n const m = contract(ctx, 'module');\n const op = await ctx.client.readContract({\n ...m,\n functionName: 'getNodeOperator',\n args: [opts.noId],\n });\n await actAs(ctx, op.managerAddress, (from) =>\n ctx.client.writeContract({\n ...m,\n functionName: 'proposeNodeOperatorManagerAddressChange',\n args: [opts.noId, opts.proposed],\n account: from,\n chain: null,\n }),\n );\n}\n\n/** Confirm the proposed manager address (signed by the proposed manager). */\nexport async function confirmManager(ctx: Ctx, opts: { noId: bigint }): Promise<void> {\n const m = contract(ctx, 'module');\n const op = await ctx.client.readContract({\n ...m,\n functionName: 'getNodeOperator',\n args: [opts.noId],\n });\n await actAs(ctx, op.proposedManagerAddress, (from) =>\n ctx.client.writeContract({\n ...m,\n functionName: 'confirmNodeOperatorManagerAddressChange',\n args: [opts.noId],\n account: from,\n chain: null,\n }),\n );\n}\n\n/** Propose a new reward address (signed by the MANAGER, per the on-chain access rule). */\nexport async function proposeReward(\n ctx: Ctx,\n opts: { noId: bigint; proposed: Hex },\n): Promise<void> {\n const m = contract(ctx, 'module');\n const op = await ctx.client.readContract({\n ...m,\n functionName: 'getNodeOperator',\n args: [opts.noId],\n });\n await actAs(ctx, op.managerAddress, (from) =>\n ctx.client.writeContract({\n ...m,\n functionName: 'proposeNodeOperatorRewardAddressChange',\n args: [opts.noId, opts.proposed],\n account: from,\n chain: null,\n }),\n );\n}\n\n/** Confirm the proposed reward address (signed by the proposed reward address). */\nexport async function confirmReward(ctx: Ctx, opts: { noId: bigint }): Promise<void> {\n const m = contract(ctx, 'module');\n const op = await ctx.client.readContract({\n ...m,\n functionName: 'getNodeOperator',\n args: [opts.noId],\n });\n await actAs(ctx, op.proposedRewardAddress, (from) =>\n ctx.client.writeContract({\n ...m,\n functionName: 'confirmNodeOperatorRewardAddressChange',\n args: [opts.noId],\n account: from,\n chain: null,\n }),\n );\n}\n","import { toHex } from 'viem';\nimport type { Hex } from '@sm-lab/receipts';\n\n/** A node-operator id packed as the on-chain `bytes8(uint64)` (big-endian). */\nexport function nodeOperatorIdBytes(noId: bigint): Hex {\n return toHex(noId, { size: 8 });\n}\n\n/** A key count packed as the on-chain `bytes16(uint128)` (big-endian). */\nexport function keyCountBytes(count: bigint): Hex {\n return toHex(count, { size: 16 });\n}\n","import { actAs } from '../act-as';\nimport { contract, type Ctx } from '../context';\nimport { keyCountBytes, nodeOperatorIdBytes } from '../encode';\n\n/** Set an operator's vetted-keys count (StakingRouter-gated). `vettedKeys` is the new absolute count. */\nexport async function unvet(ctx: Ctx, opts: { noId: bigint; vettedKeys: bigint }): Promise<void> {\n const m = contract(ctx, 'module');\n await actAs(ctx, ctx.addresses.stakingRouter, (from) =>\n ctx.client.writeContract({\n ...m,\n functionName: 'decreaseVettedSigningKeysCount',\n args: [nodeOperatorIdBytes(opts.noId), keyCountBytes(opts.vettedKeys)],\n account: from,\n chain: null,\n }),\n );\n}\n\n/** Set an operator's exited-keys count (StakingRouter-gated). `exitedKeys` is the new absolute total. */\nexport async function exit(ctx: Ctx, opts: { noId: bigint; exitedKeys: bigint }): Promise<void> {\n const m = contract(ctx, 'module');\n await actAs(ctx, ctx.addresses.stakingRouter, (from) =>\n ctx.client.writeContract({\n ...m,\n functionName: 'updateExitedValidatorsCount',\n args: [nodeOperatorIdBytes(opts.noId), keyCountBytes(opts.exitedKeys)],\n account: from,\n chain: null,\n }),\n );\n}\n","import { actAs } from '../act-as';\nimport { contract, type Ctx } from '../context';\n\n/** One withdrawn-validator record for `reportRegularWithdrawnValidators`. */\nexport interface WithdrawnValidatorInfo {\n nodeOperatorId: bigint;\n keyIndex: bigint;\n exitBalance: bigint;\n slashingPenalty: bigint;\n isSlashed: boolean;\n}\n\n/** Report a validator slashing for an operator's key (Verifier-gated). `keyIndex` is the storage index. */\nexport async function slash(ctx: Ctx, opts: { noId: bigint; keyIndex: bigint }): Promise<void> {\n const m = contract(ctx, 'module');\n await actAs(ctx, ctx.addresses.Verifier, (from) =>\n ctx.client.writeContract({\n ...m,\n functionName: 'reportValidatorSlashing',\n args: [opts.noId, opts.keyIndex],\n account: from,\n chain: null,\n }),\n );\n}\n\n/** Report a regular validator withdrawal for one key (Verifier-gated). isSlashed = slashingPenalty > 0. */\nexport async function withdraw(\n ctx: Ctx,\n opts: { noId: bigint; keyIndex: bigint; exitBalance: bigint; slashingPenalty?: bigint },\n): Promise<void> {\n const m = contract(ctx, 'module');\n const slashingPenalty = opts.slashingPenalty ?? 0n;\n const info: WithdrawnValidatorInfo = {\n nodeOperatorId: opts.noId,\n keyIndex: opts.keyIndex,\n exitBalance: opts.exitBalance,\n slashingPenalty,\n isSlashed: slashingPenalty > 0n,\n };\n await actAs(ctx, ctx.addresses.Verifier, (from) =>\n ctx.client.writeContract({\n ...m,\n functionName: 'reportRegularWithdrawnValidators',\n args: [[info]],\n account: from,\n chain: null,\n }),\n );\n}\n","import { maxUint256, toHex } from 'viem';\nimport type { Hex } from '@sm-lab/receipts';\nimport { actAs, roleMember } from '../act-as';\nimport { contract, type Ctx } from '../context';\nimport { REPORT_GENERAL_DELAYED_PENALTY_ROLE, SETTLE_GENERAL_DELAYED_PENALTY_ROLE } from '../roles';\n\n/** Report a general delayed penalty against an operator (penalty-reporter role). */\nexport async function reportPenalty(\n ctx: Ctx,\n opts: { noId: bigint; amount: bigint; penaltyType?: Hex; details?: string },\n): Promise<void> {\n const m = contract(ctx, 'module');\n const reporter = await roleMember(ctx, m, REPORT_GENERAL_DELAYED_PENALTY_ROLE);\n const penaltyType = opts.penaltyType ?? toHex(1n, { size: 32 });\n const details = opts.details ?? 'fork-penalty';\n await actAs(ctx, reporter, (from) =>\n ctx.client.writeContract({\n ...m,\n functionName: 'reportGeneralDelayedPenalty',\n args: [opts.noId, penaltyType, opts.amount, details],\n account: from,\n chain: null,\n }),\n );\n}\n\n/** Cancel a previously-reported general delayed penalty (penalty-reporter role). */\nexport async function cancelPenalty(\n ctx: Ctx,\n opts: { noId: bigint; amount: bigint },\n): Promise<void> {\n const m = contract(ctx, 'module');\n const reporter = await roleMember(ctx, m, REPORT_GENERAL_DELAYED_PENALTY_ROLE);\n await actAs(ctx, reporter, (from) =>\n ctx.client.writeContract({\n ...m,\n functionName: 'cancelGeneralDelayedPenalty',\n args: [opts.noId, opts.amount],\n account: from,\n chain: null,\n }),\n );\n}\n\n/**\n * Settle (process) an operator's general delayed penalty (penalty-settler role).\n * The `maxAmount` option is the per-operator settlement cap passed as the contract's 2nd array arg (confusingly named `bondLockNonces` in the ABI); the default `maxUint256` settles the penalty fully.\n */\nexport async function settlePenalty(\n ctx: Ctx,\n opts: { noId: bigint; maxAmount?: bigint },\n): Promise<void> {\n const m = contract(ctx, 'module');\n const settler = await roleMember(ctx, m, SETTLE_GENERAL_DELAYED_PENALTY_ROLE);\n await actAs(ctx, settler, (from) =>\n ctx.client.writeContract({\n ...m,\n functionName: 'settleGeneralDelayedPenalty',\n args: [[opts.noId], [opts.maxAmount ?? maxUint256]],\n account: from,\n chain: null,\n }),\n );\n}\n\n/** Compensate (pay off) an operator's general delayed penalty — signed by the operator manager. */\nexport async function compensatePenalty(ctx: Ctx, opts: { noId: bigint }): Promise<void> {\n const m = contract(ctx, 'module');\n const op = await ctx.client.readContract({\n ...m,\n functionName: 'getNodeOperator',\n args: [opts.noId],\n });\n await actAs(ctx, op.managerAddress, (from) =>\n ctx.client.writeContract({\n ...m,\n functionName: 'compensateGeneralDelayedPenalty',\n args: [opts.noId],\n account: from,\n chain: null,\n }),\n );\n}\n","import { actAs } from '../act-as';\nimport { contract, type Ctx } from '../context';\n\n/** Top up an operator's bond with ETH (permissionless `depositETH`), signed by the manager. */\nexport async function addBond(ctx: Ctx, opts: { noId: bigint; amount: bigint }): Promise<void> {\n const m = contract(ctx, 'module');\n const acc = contract(ctx, 'Accounting');\n const op = await ctx.client.readContract({\n ...m,\n functionName: 'getNodeOperator',\n args: [opts.noId],\n });\n await actAs(ctx, op.managerAddress, (from) =>\n ctx.client.writeContract({\n ...acc,\n functionName: 'depositETH',\n args: [opts.noId],\n account: from,\n value: opts.amount,\n chain: null,\n }),\n );\n}\n\n/**\n * Charge a penalty against an operator's bond (creating bond debt if uncovered). `Accounting.penalize`\n * is module-only, so we impersonate the module address. Returns whether the penalty was fully covered.\n */\nexport async function createBondDebt(\n ctx: Ctx,\n opts: { noId: bigint; amount: bigint },\n): Promise<{ penaltyCovered: boolean }> {\n const moduleAddress = contract(ctx, 'module').address;\n const acc = contract(ctx, 'Accounting');\n return actAs(ctx, moduleAddress, async (from) => {\n const { result, request } = await ctx.client.simulateContract({\n ...acc,\n functionName: 'penalize',\n args: [opts.noId, opts.amount],\n account: from,\n });\n await ctx.client.writeContract({ ...request, chain: null });\n return { penaltyCovered: result };\n });\n}\n","import {\n buildAddressesTree,\n ipfsOptionsFromEnv,\n pinJsonToIpfs,\n shouldAttemptPin,\n} from '@sm-lab/merkle';\nimport { curatedGateAbi, vettedGateAbi } from '@sm-lab/receipts';\nimport type { Hex } from '@sm-lab/receipts';\nimport { actAs, roleMember } from '../act-as';\nimport { resolveGate, type Ctx, type GateSelector } from '../context';\nimport { DEFAULT_ADMIN_ROLE, SET_TREE_ROLE } from '../roles';\n\nexport interface SetGateAddrsOptions {\n /** Whitelisted addresses to encode into the gate's merkle tree. */\n addresses: Hex[];\n /**\n * Gate selector passed to resolveGate. csm: 'ics' (default) | 'idvtc'; cm:\n * 'po'|'pto'|'pgo'|'do'|'eeo'|'iodc'|'iodcp' (default 'po') or a gate index; any: a raw 0x… address.\n */\n selector?: GateSelector | string;\n /** Skip IPFS pinning by supplying a precomputed cid (hermetic tests do this). */\n cid?: string;\n}\n\nexport interface SetGateAddrsResult {\n treeRoot: Hex;\n treeCid: string;\n}\n\n/** Default gate selector per module: cm → 'po' (CuratedGates[0]); csm → 'ics' (VettedGate). */\nfunction defaultSelector(ctx: Ctx): string {\n return ctx.module === 'cm' ? 'po' : 'ics';\n}\n\n/**\n * Build a gate addresses tree (OZ ['address'], via @sm-lab/merkle) and install it on the gate\n * (`setTreeParams(root, cid)`), impersonating the gate admin. The cid is pinned to IPFS\n * (env-configured: a local @sm-lab/ipfs or Pinata) unless `cid` is supplied.\n *\n * Module-agnostic (port of fork.just `set-gate-addrs` + `update-gate-tree`, both selector-driven):\n * the csm VettedGate and cm CuratedGate share a byte-identical grantRole/setTreeParams/getRoleMember\n * surface, so the only per-module differences are the resolved address, the default selector, and\n * which gate ABI carries the fragments.\n */\nexport async function setGateAddrs(\n ctx: Ctx,\n opts: SetGateAddrsOptions,\n): Promise<SetGateAddrsResult> {\n const selector = opts.selector ?? defaultSelector(ctx);\n // Both gate ABIs carry a byte-identical grantRole/setTreeParams/getRoleMember surface, so we use\n // the module's own ABI at runtime but pin the compile-time type to one constituent (viem can't\n // infer a union abi). Only those three fragments are ever called — sound for either gate.\n const abi = (ctx.module === 'cm' ? curatedGateAbi : vettedGateAbi) as typeof vettedGateAbi;\n const gate = { address: resolveGate(ctx, selector), abi } as const;\n const tree = buildAddressesTree(opts.addresses);\n const treeRoot = tree.root as Hex;\n const treeCid = opts.cid ?? (await pinTree(tree.dump(), selector));\n const admin = await roleMember(ctx, gate, DEFAULT_ADMIN_ROLE);\n\n await actAs(ctx, admin, async (from) => {\n await ctx.client.writeContract({\n ...gate,\n functionName: 'grantRole',\n args: [SET_TREE_ROLE, admin],\n account: from,\n chain: null,\n });\n await ctx.client.writeContract({\n ...gate,\n functionName: 'setTreeParams',\n args: [treeRoot, treeCid],\n account: from,\n chain: null,\n });\n });\n\n return { treeRoot, treeCid };\n}\n\nasync function pinTree(dump: unknown, selector: string): Promise<string> {\n // Guard BEFORE any network call so hermetic tests (no IPFS env) never hit the wire.\n if (!shouldAttemptPin()) {\n throw new Error(\n '@sm-lab/recipes: could not pin the gate tree — set IPFS_API_URL (a local @sm-lab/ipfs) or PINATA_* credentials, or pass opts.cid',\n );\n }\n return pinJsonToIpfs(dump, `gate-${selector}`, ipfsOptionsFromEnv());\n}\n","import { makeRewards as makeRewardsTree, shouldAttemptPin, type TreeDump } from '@sm-lab/merkle';\nimport { encodeAbiParameters, keccak256, parseAbiParameters, parseEther, toBytes } from 'viem';\nimport {\n feeDistributorAbi,\n feeOracleAbi,\n hashConsensusAbi,\n lidoAbi,\n type Hex,\n} from '@sm-lab/receipts';\nimport { actAs } from '../act-as';\nimport { contract, type Ctx } from '../context';\nimport { randomSeed } from '../random';\nimport { operatorInfo } from './operator-info';\nimport { warpTo } from './chain';\n\n/** Reward draw bounds (mirrors `mock-rewards.mjs`): per active key, in wei. */\nconst REWARD_MIN_WEI = 100_000_000_000_000_000n; // 0.1 ETH\nconst REWARD_MAX_WEI = 200_000_000_000_000_000n; // 0.2 ETH\nconst REWARD_SPAN = REWARD_MAX_WEI - REWARD_MIN_WEI; // 1e17\n/** type(uint64).max — padding id for a lone real operator (FeeDistributor non-empty-proof rule). */\nconst PAD_NO_ID = (1n << 64n) - 1n;\n/** Empty-tree sentinel root: 32 zero bytes. */\nconst ZERO_ROOT = `0x${'00'.repeat(32)}` as Hex;\n\nexport interface RewardsReport {\n /** tree.root, or ZERO_ROOT when there are no leaves. */\n treeRoot: Hex;\n /** pinned (or opts.treeCid); '' when treeRoot is zero. */\n treeCid: string;\n /** pinned (or opts.logCid); '' when treeRoot is zero. */\n logCid: string;\n /** Sum of all per-operator distributed rewards this frame. */\n distributed: bigint;\n /** Always 0n in the mock (matches the source). */\n rebate: bigint;\n /** In-memory OZ tree dump (uint256-as-string → JSON-safe) for tests / re-pin. Undefined when empty. */\n treeDump?: TreeDump;\n /** Cumulative leaves AFTER carry-forward + this frame's deltas, EXCLUDING the pad. */\n cumulatives: Map<bigint, bigint>;\n}\n\nexport interface MakeRewardsOptions {\n /** Injectable seed → deterministic per-key reward draw. Omit for fresh randomness. */\n seed?: Hex;\n /** Carry-forward input: the previous cumulative tree as a Map (or [noId, shares][]). */\n previousCumulatives?: Map<bigint, bigint> | [bigint, bigint][];\n /** Hermetic escape hatch: skip pinning the tree, use this cid. */\n treeCid?: string;\n /** Hermetic escape hatch: skip pinning the log, use this cid. */\n logCid?: string;\n /** Injectable clock (unix seconds) for the report log's block_timestamp. Defaults Date.now()/1000. */\n now?: number;\n}\n\n/**\n * Build the cumulative FeeDistributor rewards tree off on-chain operator state and a seeded mock\n * reward per active key, pin the tree + report log to IPFS (guarded), and return a typed in-memory\n * `RewardsReport`. Port of `mock-rewards.mjs` — but the OUTPUT path stays bigint-typed (no bare\n * `JSON.stringify` of bigints), and the per-key draw is fully seeded (no `Math.random`) so tests can\n * pin `treeRoot`/`distributed`.\n *\n * Carry-forward is via `opts.previousCumulatives` only (option (a) of the design). The pure\n * carry-forward logic — carry every prior leaf forward except the pad, then add this frame's deltas —\n * is identical no matter where the prior Map came from, so the fork path chains calls:\n * `makeRewards(ctx, { previousCumulatives: prev.cumulatives })`.\n * TODO(6f?): on-chain `treeRoot()` → `treeCid()` → IPFS fetch of the prior tree as a default source.\n */\nexport async function makeRewards(ctx: Ctx, opts: MakeRewardsOptions = {}): Promise<RewardsReport> {\n // The all-bigint seeded draw deliberately diverges from the source's lossy\n // `BigInt(Math.floor(Math.random() * Number(REWARD_SPAN)))`: we keccak-hash the seed (32-byte\n // random when omitted, via the shared `randomSeed`) so the draw is reproducible and full-precision.\n const seed = opts.seed ?? randomSeed();\n\n const m = contract(ctx, 'module');\n const count = (await ctx.client.readContract({\n ...m,\n functionName: 'getNodeOperatorsCount',\n })) as bigint;\n\n // --- read every operator (parallel; independent reads), then draw seeded rewards per active key.\n // REUSE operatorInfo (typed; uint32 already decodes to `number` — no Number() casts). The draw is\n // keyed on `noId` so it is order-independent; we still walk in ascending noId for stable output. ---\n const ids = Array.from({ length: Number(count) }, (_, i) => BigInt(i));\n const infos = await Promise.all(ids.map((noId) => operatorInfo(ctx, { noId })));\n\n const reportOperators: Record<string, { distributed_rewards: bigint }> = {};\n const frameDeltas = new Map<bigint, bigint>();\n let distributed = 0n;\n\n for (const [i, noId] of ids.entries()) {\n const info = infos[i]!;\n const activeKeys = info.totalDepositedKeys - info.totalWithdrawnKeys;\n if (activeKeys <= 0) continue;\n\n let opDistributed = 0n;\n for (let k = 0; k < activeKeys; k++) {\n opDistributed += REWARD_MIN_WEI + seededUint(seed, `reward:${noId}:${k}`, REWARD_SPAN);\n }\n reportOperators[noId.toString()] = { distributed_rewards: opDistributed };\n frameDeltas.set(noId, opDistributed);\n distributed += opDistributed;\n }\n\n const rebate = 0n;\n\n // --- carry-forward: prior cumulatives (pad excluded) + this frame's deltas ---\n const cumulatives = new Map<bigint, bigint>();\n for (const [noId, shares] of normalizePrevious(opts.previousCumulatives)) {\n if (noId !== PAD_NO_ID) cumulatives.set(noId, shares);\n }\n for (const [noId, delta] of frameDeltas) {\n cumulatives.set(noId, (cumulatives.get(noId) ?? 0n) + delta);\n }\n\n // --- build leaves; pad a lone real operator (FeeDistributor rejects empty proofs) ---\n const leaves = [...cumulatives.entries()].map(\n ([noId, shares]) => [noId, shares] as [bigint, bigint],\n );\n if (leaves.length === 1) leaves.push([PAD_NO_ID, 0n]);\n\n if (leaves.length === 0) {\n // Empty report: no tree, no pin. submitRewards treats ZERO_ROOT as a graceful skip.\n return { treeRoot: ZERO_ROOT, treeCid: '', logCid: '', distributed, rebate, cumulatives };\n }\n\n // Guard before the IPFS pin so hermetic tests with no IPFS env and no escape cids fail loudly\n // instead of hitting the wire. Mirrors `setGateAddrs.pinTree`. Sits AFTER the empty-report return\n // so an empty fork never needs IPFS configured.\n // shouldAttemptPin() is true by default (local-first); it returns false ONLY when\n // IPFS_API_URL points at real Pinata with no credentials. That is the canonical \"not configured\"\n // state tests can set to trigger this guard.\n const needsPin = opts.treeCid === undefined || opts.logCid === undefined;\n if (needsPin && !shouldAttemptPin()) {\n throw new Error(\n '@sm-lab/recipes: could not pin the rewards tree/log — set IPFS_API_URL (a local @sm-lab/ipfs) or PINATA_* credentials, or pass opts.treeCid + opts.logCid',\n );\n }\n\n // The report log (faithful-but-minimal mock shape) — bigints nested in operators[*] are\n // serialized by merkle's makeRewardsTree (bigintReplacer) before pinning.\n const blockTimestamp = opts.now ?? Math.floor(Date.now() / 1000);\n const log = {\n blockstamp: { block_timestamp: blockTimestamp },\n distributable: distributed,\n distributed_rewards: distributed,\n rebate_to_protocol: rebate,\n operators: reportOperators,\n };\n\n // Delegate pin and tree build to merkle's makeRewards. It returns a JSON-safe treeDump\n // (bigint leaf values serialized as decimal strings) — no local tree build needed.\n // When escape-hatch cids are both set, skip upload entirely (noUpload: true).\n // Otherwise let merkle handle the pin — it uses the same shouldAttemptPin() path guarded above.\n let treeRoot: Hex;\n let treeCid: string;\n let logCid: string;\n let treeDump: TreeDump;\n\n if (opts.treeCid !== undefined && opts.logCid !== undefined) {\n // Both escape-hatch cids supplied: still need to build the tree for treeRoot + treeDump.\n const result = await makeRewardsTree(leaves, { noUpload: true });\n treeRoot = result.treeRoot as Hex;\n treeDump = result.treeDump;\n treeCid = opts.treeCid;\n logCid = opts.logCid;\n } else if (opts.treeCid !== undefined) {\n // tree cid supplied, only pin the log\n const result = await makeRewardsTree(leaves, { log, noUpload: false });\n treeRoot = result.treeRoot as Hex;\n treeDump = result.treeDump;\n treeCid = opts.treeCid;\n logCid = result.logCid ?? '';\n } else if (opts.logCid !== undefined) {\n // log cid supplied, only pin the tree\n const result = await makeRewardsTree(leaves, { noUpload: false });\n treeRoot = result.treeRoot as Hex;\n treeDump = result.treeDump;\n treeCid = result.treeCid ?? '';\n logCid = opts.logCid;\n } else {\n // No escape-hatch cids: delegate both to merkle\n const result = await makeRewardsTree(leaves, { log });\n treeRoot = result.treeRoot as Hex;\n treeDump = result.treeDump;\n treeCid = result.treeCid ?? '';\n logCid = result.logCid ?? '';\n }\n\n return { treeRoot, treeCid, logCid, distributed, rebate, treeDump, cumulatives };\n}\n\n/** Deterministic uint in `[0, span)` from `keccak256(toBytes(\\`${seed}:${label}\\`)) % span`. */\nfunction seededUint(seed: Hex, label: string, span: bigint): bigint {\n return BigInt(keccak256(toBytes(`${seed}:${label}`))) % span;\n}\n\n/** Normalize the carry-forward input (Map or entries) to an iterable of [noId, shares]. */\nfunction normalizePrevious(\n prev: MakeRewardsOptions['previousCumulatives'],\n): Iterable<[bigint, bigint]> {\n if (!prev) return [];\n return prev instanceof Map ? prev.entries() : prev;\n}\n\n// --- submitRewards (port of OracleReport.s.sol:submitRewards) -------------------------------------\n\nconst ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' as const;\n\n/**\n * The `IFeeOracle.ReportData` struct as ONE tuple parameter — components in declaration order\n * (verified against `fixtures/receipts/src/abi/FeeOracle.ts`). `abi.encode(data)` for a single\n * struct == ABI-encoding one tuple parameter (head/tail offsets for the dynamic `string` fields);\n * passing the tuple type as a single param to `encodeAbiParameters` reproduces it. Do NOT flatten\n * into 9 top-level params — that drops the tuple offset and changes the hash. This is the exact\n * reportHash transcription trap.\n */\nconst REPORT_DATA_PARAMS = parseAbiParameters(\n '(uint256 consensusVersion, uint256 refSlot, bytes32 treeRoot, string treeCid, string logCid, uint256 distributed, uint256 rebate, bytes32 strikesTreeRoot, string strikesTreeCid)',\n);\n\n/** The 9-field oracle ReportData tuple (bigints for the uint256 fields). */\ninterface ReportData {\n consensusVersion: bigint;\n refSlot: bigint;\n treeRoot: Hex;\n treeCid: string;\n logCid: string;\n distributed: bigint;\n rebate: bigint;\n strikesTreeRoot: Hex;\n strikesTreeCid: string;\n}\n\nexport interface SubmitRewardsResult {\n /** false when treeRoot is zero (a legitimate empty-report skip). */\n submitted: boolean;\n /** present when submitted: the consensus frame's refSlot. */\n refSlot?: bigint;\n /** echo of report.treeRoot when submitted. */\n treeRoot?: Hex;\n /** the keccak256(abi.encode(data)) reached consensus on. */\n reportHash?: Hex;\n /** the consensus members that submitted (fast-lane, or the getMembers fallback). */\n members?: Hex[];\n}\n\n/**\n * Submit a `RewardsReport` (from `makeRewards`) on-chain: fund the FeeDistributor, warp to the next\n * consensus frame, build the `ReportData` tuple, reach consensus across the fast-lane members (with\n * a `getMembers` fallback), and submit the report data. Port of `OracleReport.s.sol:submitRewards`.\n *\n * Fork-only in practice (it WRITES + warps); the hermetic tests assert the call sequence, the funding\n * literals, the warp arguments, and the reportHash, not real frame advancement.\n *\n * A zero-root report (no active keys this frame) is a graceful no-op: returns `{ submitted: false }`\n * with no reads/writes, so `submitRewards(ctx, await makeRewards(ctx))` composes on an empty fork.\n */\nexport async function submitRewards(ctx: Ctx, report: RewardsReport): Promise<SubmitRewardsResult> {\n if (report.treeRoot === ZERO_ROOT) return { submitted: false };\n\n const feeDistributor = { address: ctx.addresses.FeeDistributor as Hex, abi: feeDistributorAbi };\n const oracle = { address: ctx.addresses.FeeOracle as Hex, abi: feeOracleAbi };\n const hashConsensus = { address: ctx.addresses.HashConsensus as Hex, abi: hashConsensusAbi };\n const lido = { address: ctx.addresses.lido, abi: lidoAbi };\n\n // --- 1. Fund the FeeDistributor with stETH shares if it cannot cover this frame ---\n const pending = (await ctx.client.readContract({\n ...feeDistributor,\n functionName: 'pendingSharesToDistribute',\n })) as bigint;\n if (pending < report.distributed + report.rebate) {\n const neededShares = report.distributed + report.rebate - pending;\n // X = getPooledEthByShares(neededShares); submit value = X + 1 ether; setBalance = X + 2 ether\n // (matches the source's `neededEth = X + 1 ether; _setBalance(fd, neededEth + 1 ether)`).\n // Inline the impersonation (not actAs) so the funding survives — actAs would clobber the\n // balance back to 100 ETH, which can be < the needed amount for large rewards.\n const x = (await ctx.client.readContract({\n ...lido,\n functionName: 'getPooledEthByShares',\n args: [neededShares],\n })) as bigint;\n const submitValue = x + parseEther('1');\n const fd = feeDistributor.address;\n await ctx.client.setBalance({ address: fd, value: x + parseEther('2') });\n await ctx.client.impersonateAccount({ address: fd });\n try {\n await ctx.client.writeContract({\n ...lido,\n functionName: 'submit',\n args: [ZERO_ADDRESS],\n value: submitValue,\n account: fd,\n chain: null,\n });\n } finally {\n await ctx.client.stopImpersonatingAccount({ address: fd });\n }\n }\n\n // --- 2. Warp to the next valid consensus frame (_waitForNextRefSlot) — all bigint math ---\n const [slotsPerEpoch, secondsPerSlot, genesisTime] = (await ctx.client.readContract({\n ...hashConsensus,\n functionName: 'getChainConfig',\n })) as [bigint, bigint, bigint];\n const [initialEpoch, epochsPerFrame] = (await ctx.client.readContract({\n ...hashConsensus,\n functionName: 'getFrameConfig',\n })) as [bigint, bigint, bigint];\n\n const ts = (await ctx.client.getBlock()).timestamp;\n const epoch = (ts - genesisTime) / secondsPerSlot / slotsPerEpoch;\n if (epoch < initialEpoch) {\n await warpTo(ctx, genesisTime + 1n + initialEpoch * slotsPerEpoch * secondsPerSlot);\n }\n\n const [frameRefSlot] = (await ctx.client.readContract({\n ...hashConsensus,\n functionName: 'getCurrentFrame',\n })) as [bigint, bigint];\n const frameStart =\n genesisTime + (frameRefSlot + slotsPerEpoch * epochsPerFrame + 1n) * secondsPerSlot;\n const ts2 = (await ctx.client.getBlock()).timestamp;\n if (frameStart > ts2) {\n await warpTo(ctx, frameStart);\n }\n\n // Re-read the frame AFTER the warp: the report must carry the now-current refSlot, not the\n // pre-warp one (the warp deliberately advances a full frame past `frameRefSlot`). The source\n // reads getCurrentFrame() again right after `_waitForNextRefSlot()` (OracleReport.s.sol:46);\n // inlining the wait must not drop that second read, or submitReport/submitReportData land on a\n // stale refSlot and HashConsensus reverts.\n const [refSlot] = (await ctx.client.readContract({\n ...hashConsensus,\n functionName: 'getCurrentFrame',\n })) as [bigint, bigint];\n\n // --- 3. Build ReportData (mock strikes values) + reportHash ---\n const consensusVersion = (await ctx.client.readContract({\n ...oracle,\n functionName: 'getConsensusVersion',\n })) as bigint;\n const data: ReportData = {\n consensusVersion,\n refSlot,\n treeRoot: report.treeRoot,\n treeCid: report.treeCid,\n logCid: report.logCid,\n distributed: report.distributed,\n rebate: report.rebate,\n strikesTreeRoot: strikesTreeRoot(refSlot),\n strikesTreeCid: `mock-strikes-${refSlot}`,\n };\n const hash = reportHash(data);\n\n // --- 4. Members with fast-lane → getMembers fallback (the Fixtures.sol fallback the .s.sol omits) ---\n let [members] = (await ctx.client.readContract({\n ...hashConsensus,\n functionName: 'getFastLaneMembers',\n })) as [Hex[], bigint[]];\n if (members.length === 0) {\n [members] = (await ctx.client.readContract({\n ...hashConsensus,\n functionName: 'getMembers',\n })) as [Hex[], bigint[]];\n }\n if (members.length === 0) {\n throw new Error('@sm-lab/recipes: no consensus members to reach quorum');\n }\n\n // --- 5. Reach consensus from every member, then submit the report data as members[0] ---\n // Sequential is REQUIRED, not a missed parallelization: actAs impersonates one account at a time\n // (impersonate → write → stop), and impersonation is global fork state — running these in parallel\n // would interleave the start/stop of different members. Matches the source's sequential loop.\n for (const member of members) {\n // eslint-disable-next-line no-await-in-loop -- sequential by necessity (impersonation is global state)\n await actAs(ctx, member, (from) =>\n ctx.client.writeContract({\n ...hashConsensus,\n functionName: 'submitReport',\n args: [refSlot, hash, consensusVersion],\n account: from,\n chain: null,\n }),\n );\n }\n\n const contractVersion = (await ctx.client.readContract({\n ...oracle,\n functionName: 'getContractVersion',\n })) as bigint;\n await actAs(ctx, members[0]!, (from) =>\n ctx.client.writeContract({\n ...oracle,\n functionName: 'submitReportData',\n args: [data, contractVersion],\n account: from,\n chain: null,\n }),\n );\n\n return { submitted: true, refSlot, treeRoot: report.treeRoot, reportHash: hash, members };\n}\n\n/** keccak256(abi.encode(data)) — encode the 9-field struct as ONE tuple param (see REPORT_DATA_PARAMS). */\nfunction reportHash(data: ReportData): Hex {\n return keccak256(encodeAbiParameters(REPORT_DATA_PARAMS, [data]));\n}\n\n/**\n * Mock strikes root: `keccak256(abi.encode(\"mock-strikes\", refSlot))` — TWO top-level params\n * (`string`, `uint256`), NOT a tuple. Matches the source's `abi.encode(a, b)`.\n */\nfunction strikesTreeRoot(refSlot: bigint): Hex {\n return keccak256(\n encodeAbiParameters(parseAbiParameters('string, uint256'), ['mock-strikes', refSlot]),\n );\n}\n","/**\n * cl-mock bridge — a thin `fetch` client that POSTs one validator to a running\n * `@sm-lab/cl` (`/admin/validators`). Mirrors the discipline of `tools/merkle/src/ipfs.ts`:\n * trailing-slash-stripped URL join, explicit `as` cast for the response (no DOM lib), and a\n * throw carrying status + the mock's `errors[]`. Ctx-agnostic — takes a `clMockUrl: string`, not\n * the whole `Ctx`, so it stays trivially unit-testable.\n */\n\nimport type { Hex } from '@sm-lab/receipts';\n\nexport interface SetValidatorInput {\n pubkey: Hex;\n /** Only status clActivate sets; cl-mock's full union lives in the app, not here. */\n status: 'active_ongoing';\n /** Serialized to a gwei integer string on the wire; omitted when undefined. */\n effectiveBalanceGwei?: bigint;\n}\n\ninterface ClMockResponse {\n accepted: number;\n errors: string[];\n}\n\n/**\n * POST one validator to a running `@sm-lab/cl` `/admin/validators`. Surfaces the mock's\n * `errors[]` and throws unless the single item was accepted.\n *\n * cl-mock returns 200 (clean), 207 (partial — only when `errors.length > 0` AND `accepted > 0`,\n * which is impossible for a single item), or 400 (all rejected, no `accepted` field). For one\n * item the realistic failure is 400, caught by the `!res.ok` clause. The `(accepted ?? 0) < 1`\n * guard is defensive belt-and-braces for a synthetic accepted:0 / missing-`accepted` body the mock\n * never actually emits — the `?? 0` is load-bearing, since a bare `accepted < 1` is `false` for\n * `undefined` and would let such a body through as a silent success.\n */\nexport async function setClValidator(clMockUrl: string, input: SetValidatorInput): Promise<void> {\n const url = `${clMockUrl.replace(/\\/+$/, '')}/admin/validators`;\n const body = JSON.stringify({\n pubkey: input.pubkey,\n status: input.status,\n ...(input.effectiveBalanceGwei !== undefined\n ? { effective_balance: input.effectiveBalanceGwei.toString() }\n : {}),\n });\n\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n });\n\n const json = (await res.json().catch(() => undefined)) as ClMockResponse | undefined;\n if (!res.ok || !json || (json.accepted ?? 0) < 1) {\n const errs = json?.errors?.length ? ` — ${json.errors.join('; ')}` : '';\n throw new Error(\n `@sm-lab/recipes: cl-mock rejected validator (${res.status} ${res.statusText})${errs}`,\n );\n }\n}\n","import type { Hex } from '@sm-lab/receipts';\nimport type { Ctx } from '../context';\nimport { setClValidator } from '../cl-mock';\nimport { getKeyBalance, getPubkey } from './reads';\n\nconst GWEI_PER_ETH = 1_000_000_000n;\nconst BASE_ETH_GWEI = 32n * GWEI_PER_ETH; // base 32 ETH, in gwei (32_000_000_000n)\n\nexport interface ClActivateResult {\n pubkey: Hex;\n status: 'active_ongoing';\n effectiveBalanceGwei: bigint;\n}\n\n/**\n * Read a key's pubkey + allocated balance on-chain, then mark it `active_ongoing` on a running\n * cl-mock with effective balance = 32 ETH + allocated balance, in gwei. Port of\n * `cl-mock.just:cl-activate`. Diverges from the Solidity helper's integer-ETH truncation\n * (`balances[0] / 1 ether`) by keeping full gwei precision (cl-mock stores `effective_balance`\n * in gwei) — the spec-blessed divergence.\n */\nexport async function clActivate(\n ctx: Ctx,\n opts: { noId: bigint; keyIndex: bigint },\n): Promise<ClActivateResult> {\n if (!ctx.clMockUrl) {\n throw new Error('@sm-lab/recipes: clActivate needs ctx.clMockUrl (a running cl-mock)');\n }\n // Sequential, not Promise.all: pubkey first so an empty key throws \"no key found\" before any\n // balance read (deterministic errors; no balance read on the empty-pubkey path).\n const pubkey = await getPubkey(ctx, opts);\n const allocWei = await getKeyBalance(ctx, opts);\n // BigInt `/` floors sub-gwei dust (allocWei >= 0 so floor == truncation toward zero).\n const effectiveBalanceGwei = BASE_ETH_GWEI + allocWei / GWEI_PER_ETH;\n await setClValidator(ctx.clMockUrl, {\n pubkey,\n status: 'active_ongoing',\n effectiveBalanceGwei,\n });\n return { pubkey, status: 'active_ongoing', effectiveBalanceGwei };\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"topup-B2NzJV7_.mjs","names":["DEFAULTS"],"sources":["../src/client.ts","../src/context.ts","../src/act-as.ts","../src/random.ts","../src/keys.ts","../src/recipes/add-keys.ts","../src/roles.ts","../src/recipes/deposit.ts","../src/recipes/reads.ts","../src/recipes/topup.ts"],"sourcesContent":["import { createTestClient, http, publicActions, walletActions } from 'viem';\n\n/**\n * The single viem client recipes use. An anvil test client (for setBalance /\n * impersonateAccount / increaseTime / snapshot) extended with public actions\n * (readContract / simulateContract / getChainId) and wallet actions (writeContract).\n * No `chain` is set — anvil forks vary by chainId, so writes pass `chain: null`.\n */\nexport function makeClient(rpcUrl: string) {\n return createTestClient({ mode: 'anvil', transport: http(rpcUrl) })\n .extend(publicActions)\n .extend(walletActions);\n}\n\n/** Structural type recipes depend on — satisfied by makeClient's return or a test fake. */\nexport type RecipeClient = ReturnType<typeof makeClient>;\n","import {\n accountingAbi,\n addresses as DEFAULTS,\n csModuleAbi,\n curatedGateAbi,\n lidoLocatorAbi,\n vettedGateAbi,\n} from '@sm-lab/receipts';\nimport type {\n AddressBook,\n ChainName,\n CmAddressBook,\n CsmAddressBook,\n Hex,\n ModuleName,\n} from '@sm-lab/receipts';\nimport { makeClient, type RecipeClient } from './client';\n\n/** Module-suite snapshot + the protocol addresses resolved on-chain by connect(). */\nexport type ResolvedAddresses = AddressBook & {\n stakingRouter: Hex;\n vebo: Hex;\n lido: Hex;\n withdrawalQueue: Hex;\n burner: Hex;\n};\n\nexport interface Ctx {\n client: RecipeClient;\n module: ModuleName;\n addresses: ResolvedAddresses;\n clMockUrl?: string;\n}\n\nexport interface ConnectOptions {\n module: ModuleName;\n /** Required unless `client` is injected. */\n rpcUrl?: string;\n /** Inject a prebuilt client (tests, or a shared client). */\n client?: RecipeClient;\n /** Override the module-suite snapshot; defaults to @sm-lab/receipts by chainId. */\n addresses?: AddressBook;\n clMockUrl?: string;\n}\n\nexport type CsmGateSelector = 'ics' | 'idvtc';\nexport type CmGateSelector = 'po' | 'pto' | 'pgo' | 'do' | 'eeo' | 'iodc' | 'iodcp';\nexport type GateSelector = CsmGateSelector | CmGateSelector;\n\nconst CM_SELECTORS: Record<string, number> = {\n po: 0,\n pto: 1,\n pgo: 2,\n do: 3,\n eeo: 4,\n iodc: 5,\n iodcp: 6,\n};\n\nfunction defaultSnapshot(chainId: number, module: ModuleName): AddressBook {\n for (const chainKey of Object.keys(DEFAULTS) as ChainName[]) {\n const byModule = DEFAULTS[chainKey] as Partial<Record<ModuleName, AddressBook>>;\n const book = byModule[module];\n if (book && (book as { ChainId: number }).ChainId === chainId) return book;\n }\n throw new Error(\n `@sm-lab/recipes: no default snapshot for chainId=${chainId}, module=${module} — pass addresses explicitly`,\n );\n}\n\n/**\n * The only place chain / addresses / module resolve. Prefers the baked `protocol` block\n * from the address book (zero on-chain reads) and falls back to reading the five protocol\n * addresses from LidoLocator when the block is absent.\n */\nexport async function connect(opts: ConnectOptions): Promise<Ctx> {\n const client = opts.client ?? makeClient(requireRpcUrl(opts));\n const chainId = await client.getChainId();\n const book = opts.addresses ?? defaultSnapshot(chainId, opts.module);\n\n const protocol = book.protocol\n ? {\n stakingRouter: book.protocol.stakingRouter,\n vebo: book.protocol.validatorsExitBusOracle,\n lido: book.protocol.lido,\n withdrawalQueue: book.protocol.withdrawalQueue,\n burner: book.protocol.burner,\n }\n : await resolveProtocolFromLocator(client, book.LidoLocator as Hex);\n\n return {\n client,\n module: opts.module,\n clMockUrl: opts.clMockUrl,\n addresses: { ...book, ...protocol } as ResolvedAddresses,\n };\n}\n\nasync function resolveProtocolFromLocator(\n client: RecipeClient,\n locator: Hex,\n): Promise<{ stakingRouter: Hex; vebo: Hex; lido: Hex; withdrawalQueue: Hex; burner: Hex }> {\n const loc = { address: locator, abi: lidoLocatorAbi } as const;\n const [stakingRouter, vebo, lido, withdrawalQueue, burner] = await Promise.all([\n client.readContract({ ...loc, functionName: 'stakingRouter' }),\n client.readContract({ ...loc, functionName: 'validatorsExitBusOracle' }),\n client.readContract({ ...loc, functionName: 'lido' }),\n client.readContract({ ...loc, functionName: 'withdrawalQueue' }),\n client.readContract({ ...loc, functionName: 'burner' }),\n ]);\n return { stakingRouter, vebo, lido, withdrawalQueue, burner };\n}\n\nfunction requireRpcUrl(opts: ConnectOptions): string {\n if (!opts.rpcUrl)\n throw new Error('@sm-lab/recipes: connect() needs rpcUrl (or an injected client)');\n return opts.rpcUrl;\n}\n\nconst STATIC = {\n Accounting: accountingAbi,\n VettedGate: vettedGateAbi,\n CuratedGate: curatedGateAbi,\n LidoLocator: lidoLocatorAbi,\n} as const;\ntype StaticName = keyof typeof STATIC;\n\nexport function contract(ctx: Ctx, name: 'module'): { address: Hex; abi: typeof csModuleAbi };\nexport function contract<N extends StaticName>(\n ctx: Ctx,\n name: N,\n): { address: Hex; abi: (typeof STATIC)[N] };\nexport function contract(ctx: Ctx, name: StaticName | 'module') {\n if (name === 'module') {\n // csModuleAbi anchors the shared IBaseModule surface (getNodeOperator,\n // addValidatorKeysETH, …) for BOTH modules — those fragments are byte-identical\n // across CSModule/CuratedModule, so selectors and decoding match. Only the ADDRESS\n // switches by ctx.module.\n const address = (\n ctx.module === 'cm'\n ? (ctx.addresses as CmAddressBook).CuratedModule\n : (ctx.addresses as CsmAddressBook).CSModule\n ) as Hex;\n return { address, abi: csModuleAbi };\n }\n return { address: (ctx.addresses as unknown as Record<string, Hex>)[name], abi: STATIC[name] };\n}\n\n/**\n * Resolve a gate selector to an address (the `_resolve-gate-addr` port). Accepted forms:\n * a raw `0x…` 40-hex address (any module); for csm — `ics` → VettedGate, `idvtc` →\n * IdentifiedDVTClusterGate (v3-only; throws on snapshots lacking it, e.g. mainnet/v2);\n * for cm — `po|pto|pgo|do|eeo|iodc|iodcp` or a numeric index → `CuratedGates[0..6]`.\n */\nexport function resolveGate(ctx: Ctx, selector: string): Hex {\n if (/^0x[0-9a-fA-F]{40}$/.test(selector)) return selector as Hex;\n if (ctx.module === 'cm') {\n const idx = CM_SELECTORS[selector] ?? (/^\\d+$/.test(selector) ? Number(selector) : undefined);\n if (idx === undefined)\n throw new Error(`@sm-lab/recipes: unknown cm gate selector \"${selector}\"`);\n const gate = (ctx.addresses as CmAddressBook).CuratedGates[idx];\n if (!gate) throw new Error(`@sm-lab/recipes: cm gate index ${idx} out of range`);\n return gate;\n }\n if (selector === 'ics') return (ctx.addresses as CsmAddressBook).VettedGate;\n if (selector === 'idvtc') {\n const g = (ctx.addresses as CsmAddressBook).IdentifiedDVTClusterGate;\n if (!g)\n throw new Error(\n '@sm-lab/recipes: idvtc gate not in this snapshot (v3-only; absent on mainnet/v2)',\n );\n return g;\n }\n throw new Error(`@sm-lab/recipes: unknown csm gate selector \"${selector}\"`);\n}\n","import { parseEther } from 'viem';\nimport type { Abi } from 'viem';\nimport type { Hex } from '@sm-lab/receipts';\nimport type { Ctx } from './context';\n\n/**\n * Run `fn` as a privileged account `who` on the fork. Funds it (anvil_setBalance),\n * unlocks it (anvil_impersonateAccount), runs the body, and always stops impersonating.\n * Replaces every Solidity `broadcast*` modifier. `who` is a RAW address — pass a contract\n * address (e.g. the module, verifier, stakingRouter) or a role member resolved via roleMember.\n */\nexport async function actAs<T>(ctx: Ctx, who: Hex, fn: (from: Hex) => Promise<T>): Promise<T> {\n await ctx.client.setBalance({ address: who, value: parseEther('100') });\n await ctx.client.impersonateAccount({ address: who });\n try {\n return await fn(who);\n } finally {\n await ctx.client.stopImpersonatingAccount({ address: who });\n }\n}\n\n/** Read `getRoleMember(role, 0)` — the canonical admin/governance member of an AccessControl role. */\nexport async function roleMember(\n ctx: Ctx,\n target: { address: Hex; abi: Abi },\n role: Hex,\n): Promise<Hex> {\n const member = await ctx.client.readContract({\n address: target.address,\n abi: target.abi,\n functionName: 'getRoleMember',\n args: [role, 0n],\n });\n return member as Hex;\n}\n","import { toHex } from 'viem';\nimport type { Hex } from '@sm-lab/receipts';\n\n/**\n * A 32-byte cryptographically-random seed, hex-encoded. The shared origin for every recipe that\n * needs fresh-but-reproducible-on-demand randomness (key material, reward draws, operator\n * addresses) — pass a fixed seed for deterministic output, call this to mint a fresh one.\n */\nexport function randomSeed(): Hex {\n const bytes = new Uint8Array(32);\n globalThis.crypto.getRandomValues(bytes);\n return toHex(bytes);\n}\n","import { makeDepositKeys } from '@sm-lab/keys';\nimport { concat, keccak256, toBytes } from 'viem';\nimport type { Hex } from '@sm-lab/receipts';\nimport { randomSeed } from './random';\n\n/**\n * Fixed BIP-39 mnemonic for deterministic test/recipe key derivation. NOT a production\n * key — exists only for hermetic reproducibility. The seed controls the startIndex,\n * which determines the EIP-2334 derivation path slice used.\n */\nconst RECIPE_MNEMONIC =\n 'impact exit example acquire drastic cement usage float mesh source private bulb twenty guitar neglect';\n\n/**\n * Deterministic real BLS validator keys derived from a fixed mnemonic at a seed-derived\n * startIndex. Produces genuine 48-byte G1 pubkeys + 96-byte G2 BLS signatures — NOT\n * keccak-expanded pseudo-keys. Pass a fixed `seed` for reproducible tests.\n *\n * The seed maps to a `startIndex` via `keccak256(seed) % 2^20` so different seeds yield\n * non-overlapping key ranges (1M range >> any realistic `count`).\n */\nexport async function randomKeys(\n count: number,\n seed?: Hex,\n): Promise<{ publicKeys: Hex[]; signatures: Hex[]; packedKeys: Hex; packedSignatures: Hex }> {\n const root = seed ?? randomSeed();\n const startIndex = Number(BigInt(keccak256(toBytes(root))) % 2n ** 20n);\n\n const { keys } = await makeDepositKeys({\n mnemonic: RECIPE_MNEMONIC,\n count,\n startIndex,\n // NOTE: withdrawal_credentials + fork-version DO sign into the BLS signature (the sig covers\n // a DepositMessage hash under a domain derived from the fork version). But this path never\n // verifies it: CSModule.addValidatorKeysETH → SigningKeys.saveKeysSigs only checks length +\n // non-empty (no BLS verify), and the deposit recipe uses obtainDepositData (StakingRouter-\n // impersonated), not the beacon DepositContract. So the chosen chain is arbitrary-but-valid here.\n chain: 'hoodi',\n });\n\n const publicKeys = keys.map((k) => k.pubkey as Hex);\n const signatures = keys.map((k) => k.signature as Hex);\n\n return {\n publicKeys,\n signatures,\n packedKeys: count === 0 ? '0x' : (concat(publicKeys) as Hex),\n packedSignatures: count === 0 ? '0x' : (concat(signatures) as Hex),\n };\n}\n","import type { Hex } from '@sm-lab/receipts';\nimport { actAs } from '../act-as';\nimport { contract, type Ctx } from '../context';\nimport { randomKeys } from '../keys';\n\nexport interface AddKeysOptions {\n noId: bigint;\n count: number;\n /** Injectable seed for reproducible keys. */\n seed?: Hex;\n}\n\nexport interface AddKeysResult {\n publicKeys: Hex[];\n}\n\n/**\n * Add `count` fresh validator keys to operator `noId`, paying the required bond, as the\n * operator's manager. Returns the generated pubkeys. (Port of `NodeOperators.addKeys`.)\n */\nexport async function addKeys(ctx: Ctx, opts: AddKeysOptions): Promise<AddKeysResult> {\n const m = contract(ctx, 'module');\n const acc = contract(ctx, 'Accounting');\n\n const op = await ctx.client.readContract({\n ...m,\n functionName: 'getNodeOperator',\n args: [opts.noId],\n });\n const manager = (op as { managerAddress: Hex }).managerAddress;\n\n const value = await ctx.client.readContract({\n ...acc,\n functionName: 'getRequiredBondForNextKeys',\n args: [opts.noId, BigInt(opts.count)],\n });\n\n const { publicKeys, packedKeys, packedSignatures } = await randomKeys(opts.count, opts.seed);\n\n await actAs(ctx, manager, (from) =>\n ctx.client.writeContract({\n ...m,\n functionName: 'addValidatorKeysETH',\n args: [manager, opts.noId, BigInt(opts.count), packedKeys, packedSignatures],\n account: from,\n value: value as bigint,\n chain: null,\n }),\n );\n\n return { publicKeys };\n}\n","import { keccak256, toBytes } from 'viem';\nimport type { Hex } from '@sm-lab/receipts';\n\n/** OZ AccessControl default admin role (all-zero bytes32). */\nexport const DEFAULT_ADMIN_ROLE = `0x${'0'.repeat(64)}` as Hex;\n/** MerkleGate: keccak256(\"SET_TREE_ROLE\"). */\nexport const SET_TREE_ROLE = keccak256(toBytes('SET_TREE_ROLE'));\n/** PausableWithRoles: keccak256(\"RESUME_ROLE\"). */\nexport const RESUME_ROLE = keccak256(toBytes('RESUME_ROLE'));\n/** BaseModule: keccak256(\"REPORT_GENERAL_DELAYED_PENALTY_ROLE\"). */\nexport const REPORT_GENERAL_DELAYED_PENALTY_ROLE = keccak256(\n toBytes('REPORT_GENERAL_DELAYED_PENALTY_ROLE'),\n);\n/** BaseModule: keccak256(\"SETTLE_GENERAL_DELAYED_PENALTY_ROLE\"). */\nexport const SETTLE_GENERAL_DELAYED_PENALTY_ROLE = keccak256(\n toBytes('SETTLE_GENERAL_DELAYED_PENALTY_ROLE'),\n);\n","import { maxUint256, size } from 'viem';\nimport { actAs } from '../act-as';\nimport { contract, type Ctx } from '../context';\n\n/**\n * Deposit up to `count` of an operator's depositable keys (StakingRouter-gated). Flushes pending\n * deposit-info, caps to the module's depositable count, and returns the number actually deposited\n * (derived from the keys `obtainDepositData` hands back). Throws if a positive request finds nothing\n * depositable — the Solidity helper silently no-ops there. (Port of `NodeOperators.deposit`.)\n */\nexport async function deposit(\n ctx: Ctx,\n opts: { count: number | bigint },\n): Promise<{ deposited: bigint }> {\n const m = contract(ctx, 'module');\n const requested = BigInt(opts.count);\n\n return actAs(ctx, ctx.addresses.stakingRouter, async (from) => {\n await ctx.client.writeContract({\n ...m,\n functionName: 'batchDepositInfoUpdate',\n args: [maxUint256],\n account: from,\n chain: null,\n });\n\n const summary = await ctx.client.readContract({\n ...m,\n functionName: 'getStakingModuleSummary',\n });\n const [, , depositable] = summary;\n const capped = requested < depositable ? requested : depositable;\n if (requested > 0n && capped === 0n) {\n throw new Error('@sm-lab/recipes: deposit found nothing depositable for this module');\n }\n\n const { result, request } = await ctx.client.simulateContract({\n ...m,\n functionName: 'obtainDepositData',\n args: [capped, '0x'],\n account: from,\n });\n await ctx.client.writeContract({ ...request, chain: null });\n\n const [pubkeys] = result;\n return { deposited: BigInt(size(pubkeys) / 48) };\n });\n}\n","import { size } from 'viem';\nimport type { Hex } from '@sm-lab/receipts';\nimport { contract, type Ctx } from '../context';\n\n/** One key's 48-byte pubkey from on-chain storage. Throws if no key exists at `keyIndex`. */\nexport async function getPubkey(ctx: Ctx, opts: { noId: bigint; keyIndex: bigint }): Promise<Hex> {\n const m = contract(ctx, 'module');\n const keys = (await ctx.client.readContract({\n ...m,\n functionName: 'getSigningKeys',\n args: [opts.noId, opts.keyIndex, 1n],\n })) as Hex;\n // count=1 → a single packed 48-byte pubkey; no per-48 slice needed. Guard `undefined`\n // (unscripted fake reads) before `size()` so it throws the clean error, not a viem internal.\n if (!keys || size(keys) !== 48) {\n throw new Error(\n `@sm-lab/recipes: no key found for operator ${opts.noId} at index ${opts.keyIndex}`,\n );\n }\n return keys;\n}\n\n/** Allocated balance (wei) for one key. */\nexport async function getKeyBalance(\n ctx: Ctx,\n opts: { noId: bigint; keyIndex: bigint },\n): Promise<bigint> {\n const m = contract(ctx, 'module');\n const balances = (await ctx.client.readContract({\n ...m,\n functionName: 'getKeyAllocatedBalances',\n args: [opts.noId, opts.keyIndex, 1n],\n })) as readonly bigint[];\n const wei = balances[0]; // noUncheckedIndexedAccess: guard\n if (wei === undefined) {\n throw new Error(\n `@sm-lab/recipes: no allocated balance for operator ${opts.noId} at index ${opts.keyIndex}`,\n );\n }\n return wei;\n}\n","import type { Hex } from '@sm-lab/receipts';\nimport { actAs } from '../act-as';\nimport { contract, type Ctx } from '../context';\nimport { getPubkey } from './reads';\n\n/** Per-key top-up cap (2016 ether). Matches `NodeOperators.MAX_TOPUP_PER_KEY`. */\nconst MAX_TOPUP_PER_KEY = 2016n * 10n ** 18n;\n\n/**\n * Top up the allocated balance of a single deposited key (StakingRouter-gated). Validates the key\n * exists and is not withdrawn, then writes a one-element `allocateDeposits` for it. Port of\n * `NodeOperators.increaseAllocatedBalance` — returns the `amountWei` it allocated.\n */\nexport async function increaseAllocatedBalance(\n ctx: Ctx,\n opts: { noId: bigint; keyIndex: bigint; amountWei: bigint },\n): Promise<{ amountWei: bigint }> {\n const m = contract(ctx, 'module');\n const { noId, keyIndex, amountWei } = opts;\n\n // Validate in the source's order — bounds, then withdrawn, THEN the pubkey read. The two checks\n // gate the pubkey lookup deliberately: on a real fork `getSigningKeys` reverts on an out-of-range\n // index, so reading it before the bounds check (e.g. batched in a parallel read) surfaces an\n // opaque revert instead of these precise errors (mirrors NodeOperators.s.sol:311-315).\n const op = await ctx.client.readContract({ ...m, functionName: 'getNodeOperator', args: [noId] });\n const total = (op as { totalDepositedKeys: number }).totalDepositedKeys;\n if (keyIndex >= BigInt(total)) {\n throw new Error(\n `@sm-lab/recipes: key index ${keyIndex} out of bounds (operator ${noId} has ${total} deposited keys)`,\n );\n }\n const withdrawn = await ctx.client.readContract({\n ...m,\n functionName: 'isValidatorWithdrawn',\n args: [noId, keyIndex],\n });\n if (withdrawn) {\n throw new Error(`@sm-lab/recipes: key ${keyIndex} of operator ${noId} is withdrawn`);\n }\n // count=1 → a single packed 48-byte pubkey; reuse reads.ts (same read + 48-byte guard + error).\n const key = await getPubkey(ctx, { noId, keyIndex });\n\n await actAs(ctx, ctx.addresses.stakingRouter, (from) =>\n ctx.client.writeContract({\n ...m,\n functionName: 'allocateDeposits',\n args: [amountWei, [key], [keyIndex], [noId], [amountWei]],\n account: from,\n chain: null,\n }),\n );\n\n return { amountWei };\n}\n\n/**\n * Top up every not-yet-allocated, not-withdrawn deposited key of an operator, one at a time in\n * strict ascending key-index order — `TopUpQueueOps` enforces a global FIFO queue head, so the\n * writes must be sequential and ordered (capped at `MAX_TOPUP_PER_KEY` per key). Port of\n * `NodeOperators.topUpActiveKeys` — returns the count it topped up.\n */\nexport async function topUpActiveKeys(\n ctx: Ctx,\n opts: { noId: bigint },\n): Promise<{ toppedUp: number }> {\n const m = contract(ctx, 'module');\n const { noId } = opts;\n\n const op = await ctx.client.readContract({ ...m, functionName: 'getNodeOperator', args: [noId] });\n const total = (op as { totalDepositedKeys: number }).totalDepositedKeys;\n if (total === 0) {\n throw new Error(`@sm-lab/recipes: operator ${noId} has no deposited keys`);\n }\n\n const allocated = (await ctx.client.readContract({\n ...m,\n functionName: 'getKeyAllocatedBalances',\n args: [noId, 0n, BigInt(total)],\n })) as readonly bigint[];\n // Guard the read against noUncheckedIndexedAccess: a too-short array would otherwise make\n // `allocated[i] === 0n` silently false-y for the missing tail.\n if (allocated.length !== total) {\n throw new Error(\n `@sm-lab/recipes: getKeyAllocatedBalances returned ${allocated.length} entries (expected ${total})`,\n );\n }\n\n // Reads are pulled up-front in parallel for every key — a harmless divergence from the source's\n // lazy per-key short-circuit (the extra `isValidatorWithdrawn`/`getSigningKeys` reads are free on\n // a fork and order-independent).\n const keys = await Promise.all(\n Array.from({ length: total }, (_, i) => i).map(async (i) => ({\n i,\n pubkey: (await ctx.client.readContract({\n ...m,\n functionName: 'getSigningKeys',\n args: [noId, BigInt(i), 1n],\n })) as Hex,\n withdrawn: (await ctx.client.readContract({\n ...m,\n functionName: 'isValidatorWithdrawn',\n args: [noId, BigInt(i)],\n })) as boolean,\n })),\n );\n\n // Skip already-allocated (`allocated[i] !== 0`) and withdrawn keys — the source's two `continue`s.\n const workList = keys.filter(({ i, withdrawn }) => allocated[i] === 0n && !withdrawn);\n\n // No-op without entering impersonation when nothing needs topping up (matches the\n // createOperatorGroup \"no chain call on no-op\" discipline).\n if (workList.length === 0) {\n return { toppedUp: 0 };\n }\n\n await actAs(ctx, ctx.addresses.stakingRouter, async (from) => {\n for (const { i, pubkey } of workList) {\n // eslint-disable-next-line no-await-in-loop -- sequential by necessity (TopUpQueueOps FIFO queue head; impersonation is global state)\n await ctx.client.writeContract({\n ...m,\n functionName: 'allocateDeposits',\n args: [MAX_TOPUP_PER_KEY, [pubkey], [BigInt(i)], [noId], [MAX_TOPUP_PER_KEY]],\n account: from,\n chain: null,\n });\n }\n });\n\n return { toppedUp: workList.length };\n}\n"],"mappings":"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"}
|