@nerochain/x402-aa
Paymaster-sponsored signer for the aa-native scheme.
@nerochain/x402-aa is the signer that satisfies the aa-native scheme. Plug it into @nerochain/x402-client's x402Fetch and the agent can pay merchants from an ERC-4337 SCW with sponsored gas.
Install
pnpm add @nerochain/x402-aa @nerochain/x402-client ethersUsage
import { ethers } from "ethers";
import { x402Fetch } from "@nerochain/x402-client";
import { aaNativeSigner } from "@nerochain/x402-aa";
const wallet = new ethers.Wallet(process.env.NERO_PRIVATE_KEY!);
const f = x402Fetch({
signer: aaNativeSigner({
signer: wallet,
rpcUrl: "https://rpc.nerochain.io",
bundlerUrl: "https://bundler-mainnet.nerochain.io",
paymasterUrl: "https://paymaster-mainnet.nerochain.io",
paymasterApiKey: process.env.NERO_PAYMASTER_API_KEY!,
settlementContract: "0x5eCfc64f2339992668f555918674B604F97B412D",
}),
});Options
type AaNativeSignerOptions = {
signer: ethers.Wallet | ethers.HDNodeWallet;
rpcUrl: string;
bundlerUrl: string;
paymasterUrl: string;
paymasterApiKey: string;
settlementContract: string;
entryPoint?: string; // default: NERO v0.6 EntryPoint
accountFactory?: string; // default: NERO SimpleAccountFactory
endpointFor?: (req, resource) => string;
scwSalt?: number | bigint; // default: 0
};signer— the EOA that owns the SCW. Anyethers.Walletorethers.HDNodeWallet. For browser flows, aJsonRpcSignerfrom a Web3Auth provider works in place after a cast (see the Web3Auth integration guide).rpcUrl— chain RPC.bundlerUrl— ERC-4337 bundler endpoint.paymasterUrl+paymasterApiKey— NERO paymaster endpoint and API key. From the NERO AA Platform dashboard.settlementContract— the deployedSettlementContractproxy address.entryPoint/accountFactory— defaults are correct for NERO mainnet and testnet. Override only if you're targeting a different deployment.endpointFor— overrides the canonical endpoint string used inrequestHash. Default:resource.url.scwSalt— distinguish multiple SCWs owned by the same EOA. Default:0.
What the signer does
When x402Fetch calls signer.buildPayload(requirement):
- Compute
requestHash = keccak256(abi.encode(merchant, chainId, "POST", endpoint, timestampBucket, clientNonce)). - Initialize a
SimpleAccountbuilder via theuseropSDK using the configured factory and salt. - Set paymaster options (type 0) so the bundler routes through the paymaster.
- Encode
executeBatch([approve(token, settlement, amount), settle(merchant, token, amount, requestHash)]). - Run the paymaster + signature middleware via
Client.buildUserOperation. - Pack the resulting UserOp and the
settlementCallSpecinto the V2 envelope.
The agent's process produces a fully-formed, paymaster-sponsored, signed UserOp ready for the merchant to forward to the facilitator.
Reading the SCW address
import { Presets } from "userop";
const builder = await Presets.Builder.SimpleAccount.init(wallet, rpcUrl, {
overrideBundlerRpc: bundlerUrl,
});
const scwAddress = builder.proxy?.address;The address is deterministic from the EOA and the factory. It's the same on every call; you can compute it ahead of time to fund the SCW (counterfactual) before its first UserOp.
Errors
If anything in the build / sponsor / sign flow fails (bundler unreachable, paymaster API rejected the key, simulation reverts), the signer throws a PaymentSignerError with the underlying cause attached. x402Fetch propagates the error to the caller.
try {
const res = await f(url, init);
} catch (e) {
if (e instanceof PaymentSignerError) {
// failed to construct the payload — bundler / paymaster issue
} else {
// network or other error
}
}