import { Address, Hash, Hex, PublicClient, WalletClient, Account } from 'viem'; import { C as ClientConfig, a as BaseClient, T as TransactionOptions } from './BaseClient-BjbYP0cf.js'; import { D as DvtPop } from './dvtPop-DyHgjBYw.js'; import './doc-types-471vSmPO.js'; interface OperatorClientConfig extends ClientConfig { superPaymasterAddress: Address; tokenAddress?: Address; } interface SponsorshipPolicy { globalLimit: bigint; userLimit: bigint; itemPrice: bigint; } /** * Client for Paymaster Operators (ROLE_PAYMASTER_SUPER) */ declare class PaymasterOperatorClient extends BaseClient { superPaymasterAddress: Address; tokenAddress?: Address; ethUsdPriceFeed: Address; xpntsFactory: Address; constructor(config: OperatorClientConfig); /** * Register as SuperPaymaster Operator (one-stop API). * This method handles all necessary steps: * 1. Checks prerequisites (must have ROLE_COMMUNITY) * 2. Checks and approves GToken to GTokenStaking * 3. Registers ROLE_PAYMASTER_SUPER * 4. Optionally deposits collateral to SuperPaymaster * * @param params Registration parameters * @param options Transaction options * @returns Transaction hash of role registration */ registerAsSuperPaymasterOperator(params?: { stakeAmount?: bigint; depositAmount?: bigint; }, options?: TransactionOptions): Promise; /** * Deploy a new Paymaster V4 and Register as AOA Operator (one-stop API). * This method handles: * 1. Checks prerequisites (ROLE_COMMUNITY) * 2. Predicts new Paymaster address * 3. Deploys Paymaster V4 via Factory * 4. Registers ROLE_PAYMASTER_AOA with staking * * @param params Deployment parameters * @param options Transaction options * @returns Object containing new paymaster address and transaction hashes */ deployAndRegisterPaymasterV4(params?: { stakeAmount?: bigint; version?: string; salt?: bigint; priceFeed?: Address; }, options?: TransactionOptions): Promise<{ paymasterAddress: Address; deployHash: Hash; registerHash: Hash; }>; /** * Deposit collateral (aPNTs/GToken) to SuperPaymaster. * This is a helper method used by registerAsSuperPaymasterOperator. */ depositCollateral(amount: bigint, options?: TransactionOptions): Promise; /** * Configure operator parameters (Token, Treasury). * Exchange rate is now read live from xPNTsToken.exchangeRate() at runtime. * If parameters are undefined, existing values are preserved. */ configureOperator(xPNTsToken?: Address, treasury?: Address, options?: TransactionOptions): Promise; withdrawCollateral(to: Address, amount: bigint, options?: TransactionOptions): Promise; isOperator(operator: Address): Promise; getOperatorDetails(operator?: Address): Promise; initiateExit(options?: TransactionOptions): Promise; withdrawStake(to: Address, options?: TransactionOptions): Promise; addGasToken(token: Address, price: bigint, options?: TransactionOptions): Promise; getTokenPrice(token: Address): Promise; setupPaymasterDeposit(params: { paymaster: Address; user: Address; token: Address; amount: bigint; }, options?: TransactionOptions): Promise; } interface ProtocolClientConfig extends ClientConfig { dvtValidatorAddress: Address; blsAggregatorAddress?: Address; superPaymasterAddress?: Address; } declare enum ProposalState { Pending = 0, Active = 1, Canceled = 2, Defeated = 3, Succeeded = 4, Queued = 5, Expired = 6, Executed = 7 } /** * Client for Protocol Governors and Validators (Infrastructure) */ declare class ProtocolClient extends BaseClient { dvtValidatorAddress: Address; blsAggregatorAddress?: Address; superPaymasterAddress?: Address; constructor(config: ProtocolClientConfig); /** * Create a new proposal */ createProposal(target: Address, calldata: Hex, description: string, options?: TransactionOptions): Promise; signProposal(proposalId: bigint, signature?: Hex, options?: TransactionOptions): Promise; /** * Execute a proposal with collected signatures */ executeWithProof(_proposalId: bigint, _signatures: Hex[], _options?: TransactionOptions): Promise; registerBLSKey(publicKey: Hex, options?: TransactionOptions): Promise; setProtocolFee(bps: bigint, options?: TransactionOptions): Promise; setTreasury(treasury: Address, options?: TransactionOptions): Promise; } interface OperatorStatus { isConfigured: boolean; isActive: boolean; balance: bigint; } /** * OperatorLifecycle - L3 Pattern * * Responsibilities: * 1. Managing the complete lifecycle of a Paymaster Operator * 2. Unifying setup (onboard), operation (config), and exit (withdraw) */ declare class OperatorLifecycle extends PaymasterOperatorClient { constructor(config: OperatorClientConfig); /** * Check if the account is ready to become an operator * (e.g., has GToken, has ROLE_COMMUNITY, etc.) */ checkReadiness(): Promise; /** * One-click Setup: Register + Deposit + Deploy Node * Wraps existing registerAsSuperPaymasterOperator or deployAndRegisterPaymasterV4 */ setupNode(params: { type: 'V4' | 'SUPER'; stakeAmount?: bigint; depositAmount?: bigint; }, options?: TransactionOptions): Promise; getOperatorStats(): Promise; /** * Start the exit process: Unstake from Registry/SuperPaymaster and Unlock funds */ initiateExit(options?: TransactionOptions): Promise; /** * Finalize exit: Withdraw all funds (Collateral + Rewards) */ withdrawAllFunds(to?: Address, options?: TransactionOptions): Promise; private getTokenBalance; } /** * L2 workflow — one-click "stake + register" onboarding for a DVT node (CC-36). * * The staked DVT registration path (`AAStarBLSAlgorithm.registerWithProof`, YetAnotherAA-Validator #165) * requires the operator EOA to first hold **ROLE_DVT** stake (>= `minStake` GToken, locked in the linked * GTokenStaking registry) before it may bind a node. On-chain that is a 4-step dance the DVT * `register-node.mjs` script cannot fully perform on its own — it lacks the privilege to stake an unstaked * operator. This workflow composes the existing L1 actions (`tokenActions` / `registryActions` / * `dvtOperatorActions`) into a single idempotent call that the SDK, holding both keys, CAN complete: * * 0. Resolve the PoP tuple (local BLS key → {@link buildDvtPop}, a pre-built tuple, or a `popSigner` * callback — the seam for a future KMS-TEE `/pop` endpoint) and derive `nodeId = keccak256(publicKey)`. * 1. Idempotency: if the operator already owns this node and it is registered, short-circuit success. * 2. Read `minStake` + ROLE_DVT `ticketPrice` → the GToken the operator must hold to register. * 3. (optional) `funderWallet` tops up the operator's ETH (gas) and GToken (stake) when either is low — * this is the "owner 代付" model. Without a funder, an under-funded operator throws a clear error. * 4. Operator approves GToken → GTokenStaking, then `registerRole(ROLE_DVT)` (locks the stake). Verify * `getEffectiveStake >= minStake`. * 5. Preflight `simulateContract(registerWithProof)` to catch any revert before spending gas. * 6. `registerWithProof(publicKey, popPoint, popSig)`; assert `isRegistered && nodeOperator == operator`. * * This mirrors, step-for-step, the on-chain-proven `tests/regression/onchain-evidence/dvt-register-e2e.ts`. * * SCOPE (CC-36 v1): covers nodes whose BLS secret key is held locally / in an HSM (the `blsSecretKey` or * pre-built `pop` inputs). A KMS-TEE **key-less** node cannot build its PoP here — the secret never leaves * the TEE — so it needs a KMS `/pop` endpoint that does not yet exist (cross-repo gap). The `popSigner` * callback is the forward seam for that: once KMS ships `/pop`, wire it as `popSigner` with no other change. */ interface OnboardDvtNodeParams { /** Read client. Its chain id selects the canonical address book when addresses are omitted. */ publicClient: PublicClient; /** * The operator EOA that stakes and registers (the on-chain `msg.sender` for `registerRole` and * `registerWithProof`). Must be a WalletClient with an account bound. */ operatorWallet: WalletClient; /** * Optional owner/funder wallet ("owner 代付"). When provided, tops up the operator's ETH and GToken * if either falls short of what registration needs. When omitted, an under-funded operator aborts * with a descriptive error instead of a mid-flow on-chain revert. */ funderWallet?: WalletClient; /** Local/HSM BLS secret key (32-byte hex). The PoP is built via {@link buildDvtPop}. */ blsSecretKey?: Hex; /** A pre-built PoP tuple (e.g. produced by an external signer). */ pop?: DvtPop; /** Async PoP provider — the seam for a future KMS-TEE `/pop` endpoint. */ popSigner?: () => Promise; /** DVT validator (`AAStarBLSAlgorithm`). Default: canonical `aaStarBLSAlgorithm`. */ validator?: Address; /** SuperPaymaster role Registry. Default: canonical `registry`. */ registry?: Address; /** GToken (stake asset). Default: canonical `gToken`. */ gToken?: Address; /** GTokenStaking (approval spender). Default: canonical `staking`. */ staking?: Address; /** Fund the operator's ETH when its balance is below this. Default: 0.015 ETH. */ minOperatorEth?: bigint; /** ETH amount the funder sends when topping up gas. Default: 0.03 ETH. */ topUpEth?: bigint; /** Extra GToken headroom above `minStake + ticketPrice` when topping up stake. Default: 2 GToken. */ gTokenHeadroom?: bigint; /** * Perform NO on-chain writes: run the reads, compute the funding/stake plan, simulate * `registerWithProof` when the operator is already staked, and return the {@link OnboardDvtNodeResult.plan}. * No ETH/GToken is sent, no stake is locked, no node is bound. */ dryRun?: boolean; } /** What a {@link onboardDvtNode} call WOULD do — populated only on a `dryRun`. All amounts in wei. */ interface OnboardDvtNodePlan { /** Whether the validator's staked-registration path is enabled. */ requireStake: boolean; /** GToken the operator must hold before `registerRole` (`max(validator, registry minStake) + ticket + headroom`). */ needGToken: bigint; /** ETH the funder would send to the operator (0 if already funded / no funder needed). */ wouldFundEth: bigint; /** GToken the funder would transfer to the operator (0 if already funded). */ wouldFundGToken: bigint; /** Whether a GToken→GTokenStaking approval would be submitted. */ wouldApprove: boolean; /** Whether `registerRole(ROLE_DVT)` would be submitted (false when the operator already holds it). */ wouldRegisterRole: boolean; /** Whether `registerWithProof` was simulated OK (only attempted when already staked; false otherwise). */ registerSimulated: boolean; } interface OnboardDvtNodeResult { /** `keccak256(publicKey)` — the node bound (or that would be bound in a dry run). */ nodeId: Hex; /** The node's 128-byte EIP-2537 G1 public key. */ publicKey: Hex; /** The operator EOA. */ operator: Address; /** True when the operator already owned this registered node — the flow short-circuited. */ alreadyRegistered: boolean; /** True when this call newly registered the node (false on idempotent short-circuit or dry run). */ registered: boolean; /** True when this call newly staked ROLE_DVT (false when the operator already held it). */ staked: boolean; /** `getEffectiveStake(operator, ROLE_DVT)` after staking. */ effectiveStake: bigint; /** `minStake()` the contract enforces. */ minStake: bigint; /** Tx hashes for each step actually performed. */ hashes: { fundEth?: Hash; fundGToken?: Hash; approve?: Hash; registerRole?: Hash; register?: Hash; }; /** The dry-run plan — present ONLY when `dryRun` was set. */ plan?: OnboardDvtNodePlan; } /** * Onboard a DVT node in one idempotent call: stake ROLE_DVT (funding the operator if a funder is given) * then bind the node via `registerWithProof`. See {@link OnboardDvtNodeParams} for the key model and scope. */ declare function onboardDvtNode(params: OnboardDvtNodeParams): Promise; /** * Node-only resolution of an **operator/funder EOA signer** for the DVT onboarding flow from the two * key sources DVT `register-node.mjs` supports: a raw private key held in an env var, and a * `forge cast wallet` account/keystore. This exists so an operator can reuse an existing `cast wallet` * setup instead of pasting a bare private key. * * NOTE — scope: `cast wallet` manages secp256k1 **Ethereum** keys only. The DVT node's **BLS** secret key * (which generates the public key being registered) cannot live in a cast wallet; supply it as * `blsSecretKey` hex (e.g. from its own env var) to {@link onboardDvtNode}. * * SECURITY: the `cast` path decrypts the keystore and reads the raw private key into this process's memory * so viem can sign transactions. Only run it on a host the operator controls. Prefer the `env` path in CI. * The keystore password is passed to `cast` via a MINIMAL child env (`ETH_PASSWORD`) — never on argv (which * is visible in `ps`) — and the child receives only `PATH`/`HOME`/`FOUNDRY_DIR`, not the parent's full env. */ type EoaKeySource = { type: 'privateKey'; privateKey: Hex; } | { /** * Read the private key from an environment variable. Without `var`, the first non-empty of * `OPERATOR_PRIVATE_KEY`, `ETH_PRIVATE_KEY`, `PRIVATE_KEY` is used. */ type: 'env'; var?: string; } | { /** * Export the key via `cast wallet private-key ` (Foundry). `args` mirrors the DVT script's * `CAST_WALLET_ARGS`, e.g. `['--account', 'dvt-op']` or `['--keystore', './ks.json']`. A `password`, * when given, is passed to cast via the `ETH_PASSWORD` child env var (NOT argv) so keystore * decryption is non-interactive without exposing the password in the process list. */ type: 'cast'; args: string[]; password?: string; }; /** Resolve the raw private key hex from an {@link EoaKeySource}. Node-only for the `cast` source. */ declare function resolveEoaPrivateKey(source: EoaKeySource): Promise; /** Resolve an {@link EoaKeySource} into a viem {@link Account} ready to build a WalletClient. */ declare function resolveEoaAccount(source: EoaKeySource): Promise; /** * KMS-TEE Proof-of-Possession signer for a **key-less** DVT node — the CC-37 `/pop` contract. * * For a node whose BLS secret key never leaves the TEE, the SDK cannot run {@link buildDvtPop} locally. * KMS exposes `POST {url}/pop {node_id | publicKey} → {publicKey, popPoint, popSig}` where the TEE signs the * node's OWN 128-byte EIP-2537 public key (`popPoint = hashToCurve(publicKey, BLS_POP_DST)`, `popSig = * sk·popPoint`) — the caller supplies no message, so it is not a signing oracle. This returns a `popSigner` * callback you hand straight to {@link onboardDvtNode}. * * ## Trust model — READ THIS * The node the operator will register is `nodeId = keccak256(publicKey)`, where `publicKey` comes from the * `/pop` RESPONSE. `_verifyPoP` (and {@link verifyDvtPop} here) only prove the responder knows the `sk` * behind THAT key — a compromised KMS or a MITM can return a self-consistent tuple for an ATTACKER'S key, * and the operator would stake + register the attacker's node. The ONLY defence is to **pin the expected * public key**: pass `publicKey`, and the signer rejects any response whose key differs. If you address by * `nodeId` alone (no `publicKey`), you are trusting the KMS's `node_id → key` mapping — acceptable only for * a KMS you fully control (e.g. your own board loopback). Prefer passing the node's known `publicKey`. * * On every response the signer additionally: normalizes/validates `publicKey` to 128-byte EIP-2537, * recomputes `popPoint = hashToCurve(publicKey, BLS_POP_DST)` and rejects a mismatch (enforces the RFC * convention), runs the {@link verifyDvtPop} pairing (points on-curve/non-infinity + `popSig = sk·popPoint`) * so a bad tuple fails HERE rather than after stake, and derives `nodeId` locally (never from the response). */ interface KmsPopSignerOptions { /** KMS base URL, e.g. `http://127.0.0.1:3100` (board loopback) — `/pop` is appended. Treat as trusted config. */ url: string; /** KMS-side node identifier the TEE maps to its sealed key. Provide this and/or {@link publicKey}. */ nodeId?: string; /** * The node's EXPECTED public key. Strongly recommended: when set, the signer pins it and rejects a * response for any other key — the only defence against a KMS/MITM key substitution (see trust model). */ publicKey?: Hex; /** `X-Signer-Token` (same token as the KMS BLS `/sign`), if the endpoint requires it. */ token?: string; /** Injected fetch (tests / non-browser runtimes). Defaults to the global `fetch`. */ fetchImpl?: typeof fetch; /** * Opt IN to the UNPINNED path — addressing by `nodeId` with NO expected {@link publicKey} to pin * against. This trusts the KMS's `node_id → key` mapping completely: a compromised KMS/MITM can make * you register an attacker's node (see trust model). Only acceptable for a KMS you fully control. When * `publicKey` is omitted and this is not `true`, the signer throws rather than silently trusting. */ allowUnpinnedKmsKey?: boolean; } /** Build a `popSigner` for {@link onboardDvtNode} that fetches a PoP from the KMS-TEE `/pop` endpoint. */ declare function kmsPopSigner(opts: KmsPopSignerOptions): () => Promise; export { type EoaKeySource, type KmsPopSignerOptions, type OnboardDvtNodeParams, type OnboardDvtNodePlan, type OnboardDvtNodeResult, type OperatorClientConfig, OperatorLifecycle, type OperatorStatus, PaymasterOperatorClient, ProposalState, ProtocolClient, type ProtocolClientConfig, type SponsorshipPolicy, kmsPopSigner, onboardDvtNode, resolveEoaAccount, resolveEoaPrivateKey };