Our work
We developed the smart-account contracts and integration SDK for Key Forge, including session permissions, batched execution and gas sponsorship. We kept session authority separate from ownership so an application can perform approved actions without gaining control of the account.
A player may craft an item, equip it and claim a reward within a few seconds. A trading strategy may need to act while its owner is offline. Requiring the main wallet to approve every operation interrupts both workflows. Handing the application an unrestricted private key removes that interruption at the cost of account control. Key Forge addresses the gap between those choices through temporary permissions that the smart account checks on-chain.
The project brings together ERC-4337 smart accounts, constrained session keys and optional gas sponsorship. We structured the architecture around a distinction between ownership and execution. The owner decides what an application may do and for how long. A session key signs operations inside that boundary. Bundlers deliver those operations and a Paymaster can cover their gas, but neither service becomes the authority over the user's assets. The challenge was making that separation hold through batching, concurrent activity and infrastructure failures.
Removing prompts meant defining what an approval allows
A session cannot safely mean that an application may act however it chooses until a timer expires. Time limits alone still leave a stolen key with broad access during its valid window. We defined a session as a capability with several independent restrictions. The account checks the target contract, function selector, value and relevant token limits as well as expiry and revocation. Validation accepts the request only when every required policy passes. A valid session-key signature proves who signed the request, but does not by itself authorise the requested action.
ERC-4337 provides the execution path through a UserOperation. The SDK packages an application action for the configured EntryPoint and the account routes its signature to the appropriate validator. Owner-authorised operations and session-authorised operations use separate validation paths. The Session Key Validator resolves the current policy before accepting the operation. High-risk account functions stay outside normal session authority. A temporary key cannot extend its own expiry, install a validator or change the owner merely because it can sign a valid operation.
For example, a game session can authorise one hour of access to the game's Gameplay and Inventory contracts with no native-token spending and a bounded allowance of the game token. The session key can then sign permitted actions without reopening the master wallet for each one. If malicious client code constructs a transfer to an unrelated address the account rejects the operation even when its signature is valid. Expiry is enforced using the chain's timestamp, so protection does not depend on the browser deleting the key or maintaining an accurate local clock.
Contract allowlists were only the first layer
Allowing a session to call a router says little about what it can do through that router. The same swap function might accept different tokens, recipients or price constraints. Key Forge therefore separates target and selector checks from optional argument-level policies. Specialised modules apply the configured recipient, token-pair and economic constraints. The architecture avoids placing a universal ABI interpreter inside the account. We kept application-specific decoding in bounded policy modules, separate from the account's common execution interface.
Token spending introduces a similar problem. An indirect protocol call can move assets without looking like a direct ERC-20 transfer. A generic spend counter cannot reliably infer every economic effect of arbitrary DeFi execution. The design combines narrow target and function permissions with token budgets and protocol-specific rules where needed. Security-critical cumulative counters remain on-chain. Backend quotas add application limits; the account's on-chain spending boundary remains in force during a backend outage.
Approvals require particular care because their effect can outlive the session that created them. Expiring a key does not erase an ERC-20 allowance already granted to another contract. The default policy therefore forbids approval methods or restricts them to explicit spenders and capped amounts. Exact approvals followed by an allowance reset are another supported pattern where the application flow permits it. The policy has to account for the authority an operation leaves behind as well as the assets it moves immediately.
One user action can contain several calls
A marketplace or trading workflow may need an approval, a swap and a deposit to complete one user intent. The account's executeBatch interface supports that composition, but batching increases the scope of validation. Checking the first call would leave later calls unexamined. Checking each amount independently could allow the total batch value to exceed the intended budget. We made complete batch inspection part of the authorisation boundary rather than a convenience check in the SDK.
Before execution, the batch validator inspects the complete request.
- It checks every target and function selector.
- It applies individual and aggregate value limits.
- It accounts for token spending and approval changes.
- It enforces the configured dependencies between calls.
- It rejects batches above the configured size limit.
We kept nested execution within the same permission boundary. Session self-calls are denied by default and unrestricted session-authorised delegatecall is excluded. Delegated code would execute in the account's storage context and could undermine the permission system itself. The resulting design gives developers batching within a defined execution surface while keeping account administration under owner-level control.
Fewer wallet prompts are safe only when the account can enforce what the user approved. Key Forge moves repeated authorisation into a bounded session while keeping ownership changes and broader permissions with the owner. The application gains room to act without gaining room to expand its own authority.
Removing gas friction without creating an open budget
A user can hold application tokens and still lack the network's native gas token. Requiring another purchase before a first action adds a separate funding task to the product. Key Forge addresses this through a Paymaster that can sponsor eligible operations. Gas sponsorship remains independent of account validation. The account determines whether the action is permitted. The Paymaster determines whether it will pay for execution. Approval by either component cannot replace the other.
That separation also contains failures. A compromised sponsorship signer can threaten the sponsor's gas budget, but its signature cannot make an unauthorised account call valid. The architecture keeps sponsorship keys separate from user session keys and treasury control. Sponsorship data binds to the chain, EntryPoint, sender, operation data, validity window and maximum cost. This prevents a valid authorisation from being detached from its intended context and reused for a different or more expensive request.
The application still needs protection against valid but useless operations that consume sponsored gas. The Paymaster service applies tenant and user budgets, permitted targets and selectors, gas ceilings and abuse checks. Temporary budget reservations and concurrency limits help control overlapping requests before actual costs are reconciled. Deposit monitoring and bounded top-ups address the operational side. Sponsorship can stop when reserves or market conditions cross configured limits without changing who owns the smart account.
In ERC-20 payment mode, the Paymaster pays native gas and charges an approved token. We kept its quote expiry, maximum token charge, decimal handling and settlement separate from session permissions. It does not make gas disappear. It changes how the cost is funded and presented. The approved charge and account permissions remain the boundary when sponsorship fails. Self-funded execution depends on a compatible route.
Permission storage had to balance cost with verifiability
Storing every allowed contract and function directly on-chain makes policies easy to inspect but increases persistent storage as permissions grow. Moving all policy data into a backend would reduce that storage while removing the enforcement boundary the project depends on. We split permission storage between account state and committed allowlists. Frequently read scalar values stay in account state. Larger allowlists use a Merkle root with proofs supplied alongside each operation. Stateful budgets and revocation state remain on-chain.
| Data | Location | Check |
|---|---|---|
| Expiry | On-chain | Time |
| Allowlist | Root + proof | Membership |
| Spend | On-chain | Budget |
| Revocation | On-chain | Status |
| Labels | Backend | Display |
The trade-off is explicit. Commitment-based permissions reduce persistent storage for large lists but add proof generation and calldata. They also increase the importance of agreement between the SDK and contract decoder. The TypeScript policy compiler translates a developer's permission object into the encoding and proof material expected by the validator. That canonical permission object also defines the summary presented for signing. A screen that describes one allowance while encoding another would break the user's understanding even if the contract behaved exactly as written.
The account core stays separate from application-specific policy logic. Validators and specialised modules carry that complexity, with owner-authorised installation and explicit versioning. This keeps one game's rules out of the common execution interface. Modules still belong to the account's trusted code, so modularity does not remove review obligations. It makes the scope of each permission rule easier to isolate and examine.
Owner operations use a separate nonce lane
Concurrency matters when a game, a bot and an owner all submit operations from the same account. A single sequential nonce stream can make unrelated activity wait behind one stuck request. Key Forge uses separate nonce lanes where supported by the account and EntryPoint configuration. Owner operations have a lane distinct from session activity. Separate sessions or automation strategies can progress independently while operations within each lane retain their required sequence.
This protects the owner's recovery path from the ordering dependencies of an application queue. It does not guarantee network inclusion or remove censorship risk. The owner still needs a working submission route and the revocation must take effect on-chain. Once it does, a previously signed operation from the revoked session cannot regain authority merely by remaining pending with a higher nonce. Validation uses current account state rather than the state that existed when the client originally signed.
For configurations that need emergency mass revocation the design includes an account session epoch. Each session belongs to a generation and incrementing the epoch invalidates older generations without iterating through their history. Individual revocation remains useful for removing one application's access. Indexers track the resulting events for display, but critical state can be confirmed directly through RPC. A database row marked active cannot override an on-chain revocation.
Swapping infrastructure without replacing the account
The bundler simulates and submits UserOperations, but it does not own the account. The SDK therefore separates account construction from provider routing. Chain configuration specifies the EntryPoint version, encoding, factory, RPC endpoints and confirmation policy. We made the configured EntryPoint, encoding and factory part of operation construction. Provider failover stays within that configuration, and retries retain the nonce and operation status.
The same principle applies to sponsorship and metadata. If a Paymaster stops responding the account's ownership and session rules remain intact. Another compatible Paymaster or a funded execution route may be available. If metadata services fail the SDK can read critical account state from the chain. Deterministic factory deployment also allows the SDK to calculate an account address before deployment and include creation in the first operation. The factory initialises ownership once and retains no administrative authority over the created account.
These paths need distinct user-facing failures. A session-policy rejection, sponsorship refusal, bundler rejection and target-contract revert describe different problems. The SDK exposes typed categories so the application can request renewed permission, offer a payment option or explain a failed target call as appropriate. Successful validation does not guarantee successful execution. Receipt tracking and reorganisation handling keep the displayed outcome connected to the canonical chain rather than the first provider acknowledgement.
The account enforces the approved permission
We connected the SDK policy compiler to the account validator through a shared encoding. Validation covers signatures, complete batches, allowlist proofs, amount limits and validity windows. Expired or revoked sessions cannot authorise new actions, session calls cannot reach owner-only functions, and cumulative spending remains tied to the configured cap.
Some limits remain outside the account abstraction layer. A correctly authorised trading operation can still encounter adverse ordering or slippage. Session permissions bound the supported calls, while the target calldata carries the operation's economic constraints. A valid signature alone grants neither unrestricted execution nor protection from adverse market conditions. A compromised frontend can also mislead the owner when requesting new authority. Clear permission presentation reduces that mismatch, while narrow on-chain scope limits what an already authorised session can do.
Key Forge gives application teams a shared path for account creation, permission compilation, operation construction and sponsored execution. The scope of each integration remains concrete. Developers define the actions their application needs, choose the relevant policy modules and expose the resulting permission summary to the owner. The account can then evaluate every operation against that approved boundary without requiring the main wallet to repeat the same decision for each permitted action.
See our architecture in practice.
DEVLAB · ARCHITECTURE EXAMPLE
Agent
Commerce
A look inside the software architecture behind Agent Commerce.
View architecture