An autonomous agent wants to place an order on your API. Before you let it through, you want one thing: if it misbehaves, can you make it pay? CAPTCHAs answer "are you human?" Passwords answer "do you have credentials?" Signet answers a different question entirely: "are you solvent?" This is the design for a performance-bond layer that replaces behavioral verification with economic accountability.
Context
Economic accountability for autonomous agents
By the mid-2020s, a material fraction of web traffic is automated. AI agents book flights, buy tickets, place orders, and call APIs without a human in the loop. The infrastructure they hit was designed for humans. CAPTCHAs block legitimate automation. IP reputation lists are reactive, not proactive. Nothing in the stack asks whether the agent has skin in the game.
The insight behind Signet is that performance bonds already solve this problem in traditional commerce. A contractor who wants to bid on a construction project posts a bond. If they default, the bond pays the client. Signet applies that logic on-chain: an agent stakes USDC as collateral before it is allowed to transact. Misbehavior triggers programmatic slashing. The bond is not a gate; it is a guarantee. A platform does not need to know whether the agent is human, legitimate, or well-intentioned. It only needs to know whether the agent has posted enough collateral to cover the potential damage.
This is a design-stage project. I built it solo, working in Solidity and Foundry, without a team or external funding. Two of the three contracts are implemented and deployed to Ethereum Sepolia testnet. The third, SignetResolver, exists as a detailed specification in the PRD but has no code yet. The frontend dashboard, SDK, Ponder indexer, and REST API are built and running locally. Nothing is on mainnet. The portfolio case study is here because the design problem and the tradeoffs involved are worth examining, not because the system is running in production.
Architecture
Three contracts, two of them real
Signet is built around three smart contracts. Registry handles identity. Vault handles economic security. Resolver handles cross-chain verification. The first two exist as Solidity. The third exists as a design.
The split was intentional. The Registry and Vault together cover the core mechanism: an agent can register a DID, stake USDC, get slashed if it misbehaves, and go through a 7-day unbonding period before withdrawing. That is the loop worth validating first. Cross-chain verification is an important feature for a deployed system, but it is not the thing that proves or disproves the economic model. Writing the Resolver spec before the core mechanism was solid would have been the wrong order.
Triangle topology. Arbitrum holds Registry and Vault contracts; Base and Optimism are planned destinations for cross-chain verification via LayerZero.
The deployment sequence followed the same logic. Registry and Vault are deployed to Ethereum Sepolia (Arbitrum Sepolia faucet access was pending during development) and verified on Etherscan. The test suite runs 90 unit tests against both contracts. The Resolver is documented in PRD.md §3.3 with a full Solidity interface and a LayerZero integration pattern, but no code has been written for it.
01 · Registry: on-chain agent identity
Every agent in Signet gets a decentralized identifier of the form did:signet:arbitrum:0x.... The Registry contract is the on-chain source of truth for this identity. It stores the owner address, the DID string, a human-readable name, a reputation score from 0 to 1000, a creation timestamp, and an active/inactive flag.
Registration costs a $10 fee and mints a new agentId (a bytes32 hash). The owner can update the agent's name, deactivate it permanently (provided the stake balance is zero), or transfer ownership. Events are emitted on every state change so that the Ponder indexer can keep an off-chain GraphQL replica current.
The reputation score is not a social metric. It starts at 500, accrues through age and transaction volume, and is reduced by slashing events. A score below 300 puts the agent in probationary status. Platforms can gate access by score tier without touching the stake logic directly.
SignetRegistry.sol is deployed, tested, and verified on Sepolia.
02 · Vault: stake, unstake, slash
The Vault is where the economic model lives. An agent stakes USDC by calling stake(agentId, amount). The minimum is $100. To withdraw, the agent calls requestUnstake, which starts a 7-day unbonding period. During those 7 days the stake can still be slashed. Only after the timer expires can the agent call withdraw and recover the funds. The unbonding delay is the mechanism that prevents an agent from front-running a fault claim by pulling its stake the moment it misbehaves.
Slashing is executed by the ArbitrationEngine (a restricted caller). The formula:
// SignetVault.sol - slashing formula (pseudocode)
slashAmount = min(totalStake, claimedDamage * 1.2)
victimCompensation = slashAmount * 0.8
protocolRevenue = slashAmount * 0.2The 1.2x multiplier on claimed damage does two things. It ensures the victim is made whole even if the arbitration estimate is slightly conservative, and it creates a small surplus that funds the protocol. The 80/20 split between victim compensation and protocol revenue reflects the same structure used in many commercial bonding arrangements. The multiplier also matters for game theory: an agent cannot game the slashing system by understaking relative to the damage it can cause, because any validated claim draws 120% of the stated damage.
SignetVault.sol is deployed, tested, and verified on Sepolia alongside the Registry.
03 · Resolver: cross-chain verification (spec-only)
The Resolver is the third contract in the design, and it does not exist as code yet. This section describes what it would do.
The problem it solves: a merchant on Base or Optimism wants to verify that an agent has a valid Signet stake on Arbitrum. Making a direct RPC call to Arbitrum on every request adds latency and creates a hard dependency on Arbitrum's availability. The Resolver architecture avoids this by syncing a merkle root of all stake state from Arbitrum to lightweight resolver contracts on each target chain, via LayerZero V2, approximately once per hour.
On the remote chain, verification is a local operation: the agent presents a merkle proof, the resolver checks it against the cached root, and returns a boolean. The proof is valid for up to one hour. If the root is stale or a LayerZero message fails, the SDK falls back to a direct RPC call to Arbitrum and flags the result as "unverified" in the response.
The security assumption here is worth naming explicitly: LayerZero's Ultra Light Node V2 is a trusted relayer. The protocol inherits whatever trust assumptions ULN V2 carries. That is an acceptable tradeoff for the use case (verification latency), but it is not trustless. The full specification is in PRD.md §3.3, including the StakeProof struct, lzReceive handler, and verifyStake function signature.
Designed, not yet implemented.
2 of 3
contracts implemented (Registry + Vault); Resolver is spec-only
7 days
unbonding period before stake withdrawal is allowed
1.2x
slash multiplier applied to claimed damage
80/20
victim compensation / protocol revenue split on each slash
4
fault types: SPAM, SCALPING, FRAUD, RATE_LIMIT_ABUSE
90
unit tests passing against Registry and Vault on Sepolia
Learnings
- Chose Foundry over Hardhat after reading both docs side by side. Foundry's test runner executes tests as Solidity, not JavaScript, which means the test code is closer to the contract code. Gas snapshots and fuzzing are built in. The learning curve is steeper but the feedback loop during test-driven contract development is noticeably tighter.
- Wrote the full PRD before writing any Solidity. This was the right call. The slashing formula, the fault types, the unbonding period, and the arbitration flow all got stress-tested on paper first. By the time I opened a Solidity file, the state transitions were already settled. The Resolver spec in particular would have been much harder to design mid-implementation.
- The 1.2x slash multiplier needed justification beyond 'it felt right.' The argument: if an arbitrator estimates damage conservatively (which is predictable behavior under uncertainty), a 1.0x multiplier systematically undercompensates victims. The 0.2x surplus also funds the protocol, which gives the operator an incentive to process claims quickly. Without the surplus, the protocol's incentive to adjudicate is weaker.
- LayerZero's relayer trust assumption is the thing I would want to resolve before any mainnet deployment. ULN V2 is not trustless. If the relayer is compromised, stale or forged merkle roots could let undercollateralized agents pass verification on remote chains. The mitigation in the spec (timestamp checks, SDK-level fallback, flagging unverified results) reduces the risk window but does not eliminate it. This is an open design question, not a solved one.
FAQ
- Is this on mainnet?
- No. SignetRegistry and SignetVault are deployed to Ethereum Sepolia testnet and verified on Etherscan. There is no mainnet deployment. The roadmap targets Arbitrum One mainnet after a professional audit and a testnet bug bounty, neither of which has happened yet. This is a design exercise with working contract implementations, not a running production system.
- Why Arbitrum?
- Three reasons: low gas fees compared to Ethereum mainnet, full EVM compatibility so standard Solidity tooling works without modification, and a mature L2 ecosystem with established bridges and indexer support. Slashing events and staking transactions need to be cheap enough that small agents ($100 minimum stake) are not priced out by gas. On mainnet Ethereum, a stake transaction at peak gas would be a meaningful fraction of the minimum stake.
- Why not Optimism or Base for the primary chain?
- Arbitrum has deeper liquidity for USDC and more established tooling for the Ponder indexer at the time of design. The difference between Arbitrum and Optimism/Base is not large enough to matter for the core mechanism, but Arbitrum was the more conservative choice for a first deployment. The cross-chain Resolver design specifically targets Base and Optimism as secondary chains, so the architecture assumes Arbitrum as the source of truth and both others as consumers.
- Why LayerZero over Chainlink CCIP for cross-chain verification?
- For the Resolver's use case, the relevant metric is cost per message, not data feed latency. Syncing a merkle root once per hour costs approximately $0.50 per update via LayerZero. CCIP is priced differently and generally higher for arbitrary message passing. The tradeoff is trust model: CCIP's oracle network has different trust assumptions than LayerZero's ULN V2. Neither is fully trustless. LayerZero was the cheaper path for a design that syncs infrequently.
- What would it take to deploy to mainnet?
- At minimum: a professional security audit (Sherlock or Code4rena, estimated $15K), a public testnet bug bounty period, implementation of the SignetResolver contract, and a TVL cap for the first deployment period to limit exposure. The arbitration engine in V1 is centralized (multisig), which is acceptable for a pilot but needs a path to decentralization. Legal opinion on whether the performance bond model triggers financial services regulation in Malaysia and Singapore is also outstanding.