EIP-7702 and EIP-7212 allow users to delegate control over an EOA to a P256 key. This has large potential for UX improvement as P256 keys are adopted by commonly used protocols like Apple Secure Enclave and WebAuthn.
The traditional flow of crypto onboarding experience can feel cumbersome: Users have to setup a wallet, back up their mnemonic phrase and make sure to keep it safe. What if there was a simpler and more secure way to manage private keys? Passkeys have already solved this problem by allowing users to authenticate using methods like Touch ID while keeping passwords safe. These keys are generated within secure hardware modules, such as Apple's Secure Enclave or a Trusted Platform Module (TPM), which are isolated from the operating system to protect them from being exposed.
EIP-7212 introduces a precompile for the secp256r1 elliptic curve, a curve that is widely used in protocols like Apple Secure Enclave and WebAuthn. EIP-7702 introduces a new transaction type, allowing externally owned accounts (EOAs) to function like a smart contract. This unlocks features such as gas sponsorship, transaction bundling or granting limited permissions to a sub-key.
This example demonstrates how the upcoming EIP's EIP-7720 and EIP-7212 (already live in Odyssey's Chapter 1), will enable you to use a passkey to sign an onchain message, improving the onboarding experience for crypto novices using your Dapp.
- Run anvil in Odyssey mode to enable support for EIP-7702 and P256 precompile:
anvil --odyssey
Anvil automatically generates pre-funded dev accounts, for the example below we will be using dev account with address 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
and private key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
- Now generate a P256 private and public key pair using the python script in the repo
python p256.py gen
This script will generate a p256
private and public key pair, save them to private.pem
and public.pem
respectively, and print the keys in hex format.
- Deploy a P256Delegation contract, which we will be delegating to
forge create P256Delegation --private-key "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
- Send an EIP-7702 transaction, which delegates to our newly deployed contract. This transaction will both authorize the delegation and set it to use our P256 public key that we have generated previously:
export DELEGATE_ADDRESS=<enter-delegate-contract-address>
export PUBKEY_X=<enter-public-key-x>
export PUBKEY_Y=<enter-public-key-y>
cast send 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 'authorize(uint256,uint256)' $PUBKEY_X $PUBKEY_Y --auth $DELEGATE_ADDRESS --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
- Verify that new code at our EOA account contains the delegation designation, a special opcode prefix to highlight the code has a special purpose:
$ cast code 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
0xef0100...
- Prepare signature to be able to transact on behalf of the EOA account by using the
transact
function of the delegation contract. Let's generate a signature for sending 1 ether to the zero address by using our P256 private key:
python p256.py sign $(cast abi-encode 'f(uint256,address,bytes,uint256)' $(cast call 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 'nonce()(uint256)') '0x0000000000000000000000000000000000000000' '0x' '1000000000000000000')
Let’s look at the command step-by-step
python p256.py sign
function signs the message with our previously generated p256 keycast abi-encode 'f(uint256,address,bytes,uint256)
abi-encodes the payload expected by theP256Delegation
contract with the following fields- nonce:
$(cast call 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 'nonce()(uint256)')
is used to fetch the nonce from our EOA to protect against replay attacks - address:
0x0000000000000000000000000000000000000000
- bytes:
0x
- amount:
1000000000000000000
wei (= 1 ETH)
- nonce:
The command output will respond with the signature r and s values.
- Send the message including signature via the
transact
function of the delegation contract:
# use dev account
export SENDER_PK=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
export SIG_R=<enter-signature-r>
export SIG_S=<enter-signature-s>
cast send 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 'transact(address to,bytes data,uint256 value,bytes32 r,bytes32 s)' '0x0000000000000000000000000000000000000000' '0x' '1000000000000000000' $SIG_R $SIG_S --private-key $SENDER_PK
Note that, similarly to simple-7702 example, there is no restriction on who could submit this transaction.