NiceTry: A Frame Transaction-ready Post Quantum Wallet
By Alessandro Baiocchi

NiceTry: A Frame Transaction-ready Post Quantum Wallet
Introduction
When looking at Ethereum and other EVM chains, it seems clear that post-quantum security runs through account abstraction. Most teams working on these problems have converged on this, and as more concrete proposals appear, no other path looks viable.
As we build our post-quantum Ethereum wallet infra 'NiceTry', which is the reference implementation of our Ephemeral Keys Protocol, we started experimenting with what could be the future of account abstraction: frame transactions.
In this article we cover how NiceTry works under frame transactions, and how the implementation differs from the canonical ERC-4337 version.
What is NiceTry
NiceTry is a wallet infrastructure project that acts as the reference implementation of our ephemeral-key post-quantum protocol.
Following the core design, the user's account address stays stable, while the signing key changes over time. Each transaction is authorized by a short-lived key, and successful validation must force the account to move to the next key. The current NiceTry implementation uses FORS+C, a hash-based signature, as the primary signature scheme.
In the ERC-4337 version, NiceTry is implemented as a smart account whose validateUserOp checks a FORS signature and rotates the account owner during validation. With frame transactions this logic changes, and instead of UserOperation, EntryPoint, and validateUserOp, the account expresses its validation logic inside the frame transaction model.
The NiceTry frame account design keeps the same security goal, but implements it differently to match the frame transaction's standard: validation checks the FORS signature, then requires the very next frame to rotate the signer.
What are Frame Transactions
EIP-8141 introduces a new transaction type built from a sequence of frames. Instead of one transaction containing one top-level call, a frame transaction contains multiple framed calls, each with its own mode, target, gas limit, value, and calldata.
Conceptually, a frame transaction looks like this:
tx = {
sender,
nonce,
frames: [
[mode, flags, target, gas_limit, value, data],
...
],
fee fields
}
The modes relevant to NiceTry are:
VERIFY validate the transaction
SENDER execute as the transaction sender
DEFAULT execute through the frame transaction entry context
The important change is that account validation becomes native. A smart account can define its own validation logic in a VERIFY frame, instead of relying on an ECDSA signature over a normal transaction or on ERC-4337's validateUserOp.
This is the key difference for NiceTry: the VERIFY frame can approve execution, but it cannot directly rotate the signer because the VERIFY frame itself is bound to be static: it can execute any code that is necessary to verify the signature and read all necessary storage, but cannot change the state in doing so. Rotation has to modify the state by updating the contract's owner, therefore has to happen in a later execution frame, and validation must ensure that this later frame exists.
ERC-4337 Account vs Frame Account
The ERC-4337 account and the frame account enforce the same high-level rule, but the execution model is different.
| Area | ERC-4337 NiceTry Account | Frame NiceTry Account |
|---|---|---|
| Transaction object | UserOperation | Native frame transaction |
| Validation entry point | validateUserOp called by EntryPoint | VERIFY frame |
| Execution entry point | Account execute / calldata from the UserOperation | Later SENDER frames |
| Signature checked against | userOpHash | Frame transaction signature hash |
| Rotation timing | Can happen during account validation | Cannot happen in VERIFY; must happen in a later frame |
| Approval mechanism | Return value / EntryPoint validation semantics | APPROVE instruction |
| User execution dependency | EntryPoint executes after validation | SENDER frames execute only after approval |
| Frame inspection | Not applicable | Account can inspect the next frame before approval |
In ERC-4337, signer rotation can be part of validateUserOp. Validation happens before user execution, so if user execution later fails, the signer has still rotated.
In frame transactions aVERIFY frame is static, so the account cannot update owner during validation. If NiceTry only checked a FORS signature and called APPROVE, it would approve execution without forcing rotation, breaking the ephemeral-key invariant.
The frame account therefore uses a different pattern:
Frame 0: VERIFY - check FORS signature and require Frame 1 to be rotation
Frame 1: SENDER - call rotateOwner(nextOwner)
Frame 2+: SENDER - user actions
Rotation Invariant
The security invariant is:
No frame transaction should be approved unless signer rotation is already scheduled as the next frame.
NiceTry enforces that invariant inside the frame account validation path.
During the VERIFY frame, the account first checks the FORS signature against the current owner. If the signature is invalid, validation fails.
If the signature is valid, the account does not immediately approve the transaction. It inspects the next frame and requires it to be the rotation frame.
The next frame must satisfy all of these checks:
mode == SENDER
target == address(this)
value == 0
atomic == false
selector == rotateOwner(address)
nextOwner != address(0)
In plain English: the frame immediately after validation must be a self-call from the account to rotateOwner(address), with no ETH transfer, no atomic batching, and a nonzero replacement owner.
Only after those checks pass does validation approve execution and payment.
The resulting flow is:
1. VERIFY frame runs on the NiceTry frame account.
2. Account verifies the FORS signature.
3. Account inspects frame N + 1.
4. Account confirms frame N + 1 is exactly rotateOwner(nextOwner).
5. Account approves execution/payment.
6. The next SENDER frame rotates the owner.
7. User execution frames run after rotation.
The interaction looks like this:
This interaction flow preserves the important property from the ERC-4337 design: even if later user execution fails, the signer rotation has already happened. The used FORS key is not left as the account's active key for a future transaction.
Therefore, the frame account is not relying on wallet discipline or off-chain convention, and it is not saying "clients should include a rotation frame." What it does is making rotation a validation requirement, and if the transaction does not put rotation immediately after verification, the account refuses to approve the transaction.
One small side effect of this change of paradigm is that, as of the current EIP-8141 spec state, this kind of account cannot be used as a paymaster, as it would not be able to add the rotation frame for itself to the user’s transaction.
Conclusions
Frame transactions do not remove the need for smart account logic, instead, they move the account validation surface closer to the protocol. For NiceTry, that is exactly the interesting part: the same ephemeral-key invariant can be expressed without relying on ERC-4337's EntryPoint flow.
The main difference is that signer rotation cannot happen inside the VERIFY frame itself, because VERIFY execution is bound to be static. The account has to split validation and rotation into two phases: first prove that the transaction is authorized, then require that the next executable frame performs the rotation.
The resulting model is simple:
VERIFY -> mandatory rotateOwner(nextOwner) -> user execution
We believe that native account abstraction is a step forward for Ethereum, eliminating the need for a heavy infrastructure surrounding current account abstraction. The current state of the EIP-8141 proposal brings some restrictions to the possibilities of the validation flow, which as we've shown in this article require a different account paradigm to keep invariants intact.