Toward a “default” delegate wallet under EIP‑7702
EIP‑7702 lets an EOA point to on‑chain contract code that runs on every future transaction until the user swaps it out with another 0x04. That’s powerful, but if each wallet ships a different delegate implementation we fragment relayers, dApps and audit tooling.
I’m circulating a candidate baseline—Minimal Avatar Smart Wallet (MASW)—for feedback before submitting an ERC. Below is the condensed tech spec repo GitHub - MostafaS/MASW: Minimal Avatar Smart Wallet.
MASW at a glance
Aspect | Design choice |
---|---|
Core function | executeBatch(targets[arr ], values[ arr], calldatas [arr], token, fee, expiry, signature) |
Fee model | Relayer reimbursed inside the call (ETH or ERC‑20) |
Replay guard | Global metaNonce + expiry + chain‑bound EIP‑712 domain separator |
Extensibility | Exactly two plug‑ins: |
• PolicyModule – pre/post guards (spend caps, blacklists, etc.) | |
• RecoveryModule – ERC‑1271 guardian/social‑recovery signatures | |
Upgradability | None: byte‑code is immutable; user changes behaviour via another 0x04 |
Security bits | Re‑entrancy guard, OZ‑style ERC‑20 return check, NFT receivers built‑in |
Quick technical spec
// EIP‑712 type hash
BATCH_TYPEHASH = keccak256(
"Batch(address[] targets,uint256[] values,bytes[] calldatas,address token,uint256 fee,uint256 exp,uint256 metaNonce)"
);
// Domain separator binds (name="MASW", version="1", chainId, owner)
DOMAIN = keccak256(EIP712Domain(...));
// Storage slots
0: metaNonce
1: _entered (re‑entrancy flag)
2: policyModule
3: recoveryModule
immutables: owner, DOMAIN
Execution flow
-
Check arrays match length > 0; block.timestamp <= expiry.
-
Verify EIP‑712 signature → owner or recoveryModule.
-
Increment metaNonce (prevents grief replay).
-
If policyModule != 0, run preCheck.
-
Loop through (targets[i].call{value:values[i]}(calldatas[i])); revert on first failure.
-
Run postCheck (same semantics).
-
Pay relayer:
token == 0 → native transfer; else ERC‑20 transfer. -
Emit BatchExecuted(structHash).
Interfaces:
interface IPolicyModule {
function preCheck (address sender, bytes calldata) external view returns (bool);
function postCheck(address sender, bytes calldata) external view returns (bool);
}
interface IRecoveryModule {
function isValidSignature(bytes32 hash, bytes calldata sig) external view returns (bytes4);
}
Hashing helpers use keccak256(abi.encodePacked(arr))
, matching ethers.js
packed‑array encoding—signatures generated off‑chain verify on‑chain without friction.
What MASW is not
- Intent resolver – unlike ERC‑7806, MASW runs exactly what the user signed, no relayer path‑finding.
- Runtime introspection standard – the delegate itself doesn’t expose a callContext7702() view; that could live in a PolicyModule if the ecosystem agrees it’s useful.
- Upgrade‑by‑delegatecall – deliberate omission to minimise audit surface; changing logic means pointing to a new code hash
If the design survives scrutiny here I’ll submit the ERC PR. Tear it apart—edge cases, prior art, gas tricks all welcome. Thanks!