-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add ERC20 as fee * add feeToken to ERC20 as fee tutorial * add more description * address comments * Update README.md --------- Co-authored-by: Qi Zhou <[email protected]>
- Loading branch information
Showing
3 changed files
with
117 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.23; | ||
|
||
interface IERC20 { | ||
function transfer(address _to, uint256 _value) external returns (bool success); | ||
} | ||
|
||
contract TestERC20 { | ||
mapping (address => uint256) public balance; | ||
|
||
function transfer(address _to, uint256 _value) public returns (bool success) { | ||
balance[msg.sender] -= _value; | ||
balance[_to] += _value; | ||
return true; | ||
} | ||
|
||
function mint(address _to, uint256 _value) public { | ||
balance[_to] += _value; | ||
} | ||
} | ||
|
||
/// @notice Contract designed for being delegated to by EOAs to authorize an ERC20 transfer with ERC20 as fee. | ||
contract ERC20Fee { | ||
|
||
/// @notice Internal nonce used for replay protection, must be tracked and included into prehashed message. | ||
uint256 public nonce; | ||
|
||
/// @notice Main entrypoint to send tx. | ||
function execute(address to, bytes memory data, uint256 value, IERC20 feeToken, uint256 fee, uint8 v, bytes32 r, bytes32 s) public { | ||
bytes32 digest = keccak256(abi.encode(nonce++, to, data, value, feeToken, fee)); | ||
address addr = ecrecover(digest, v, r, s); | ||
|
||
require(addr == address(this)); | ||
|
||
(bool success,) = to.call{value: value}(data); | ||
require(success, "call failed"); | ||
require(feeToken.transfer(msg.sender, fee)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# Simple 7702 demo to pay gas fee using ERC20 | ||
|
||
This example demonstrates how EIP-7702 allows Alice to authorize a smart contract to execute an ERC20 transfer and pay fee in ERC20 to Bob, who sponsors the gas fees for a seamless experience. | ||
|
||
## Steps involved | ||
|
||
- Start local anvil node with Odyssey features enabled | ||
|
||
```bash | ||
anvil --odyssey | ||
``` | ||
|
||
- Anvil comes with pre-funded developer accounts which we can use for the example going forward | ||
|
||
```bash | ||
# using anvil dev accounts | ||
export ALICE_ADDRESS="0x70997970C51812dc3A010C7d01b50e0d17dc79C8" | ||
export ALICE_PK="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" | ||
export BOB_PK="0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a" | ||
export BOB_ADDRESS="0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" | ||
export CHARLES_ADDRESS="0x90F79bf6EB2c4f870365E785982E1f101E93b906" | ||
``` | ||
|
||
- Deploy ERC20 and mint tokens | ||
```bash | ||
forge create TestERC20 --private-key $BOB_PK | ||
export ERC20=<ERC20 addr> | ||
cast send $ERC20 'mint(address,uint256)' $ALICE_ADDRESS 10000000000000000000 --private-key $BOB_PK # 10E9 tokens | ||
cast call $ERC20 'balance(address)' $ALICE_ADDRESS | ||
``` | ||
|
||
- We need to deploy a contract which verifies the user signature and execute ERC20 transfers.: | ||
|
||
```bash | ||
forge create ERC20Fee --private-key $BOB_PK | ||
|
||
export ERC20_FEE="<enter-contract-address>" | ||
``` | ||
|
||
- Alice (delegator) can sign a message which will delegate all calls to her address to the bytecode of smart contract we've just deployed. | ||
|
||
First, let's verify that we don't have a smart contract yet associated to Alice's account, if that's the case the command below should return a `0x` response: | ||
|
||
```bash | ||
$ cast code $ALICE_ADDRESS | ||
0x | ||
``` | ||
|
||
|
||
- Alice can sign an EIP-7702 authorization using `cast wallet sign-auth` as follows: | ||
|
||
```bash | ||
SIGNED_AUTH=$(cast wallet sign-auth $ERC20_FEE --private-key $ALICE_PK) | ||
``` | ||
|
||
- Alice can sign an off-chain data to authorize anyone to send ERC20 on behave of Alice in exchange of ERC20 fee | ||
|
||
```bash | ||
ERC20_TRANSFER_CALLDATA=$(cast calldata 'transfer(address,uint256)' $CHARLES_ADDRESS 1000000000000000000) | ||
SIGNED=$(cast wallet sign --no-hash $(cast keccak256 $(cast abi-encode 'f(uint256,address,bytes,uint256,address,uint256)' 0 $ERC20 $ERC20_TRANSFER_CALLDATA 0 $ERC20 1000)) --private-key $ALICE_PK) | ||
V=$(echo $SIGNED | cut -b 1-2,131-132) | ||
R=$(echo $SIGNED | cut -b 1-66) | ||
S=$(echo $SIGNED | cut -b 1-2,67-130) | ||
``` | ||
|
||
- Bob (delegate) relays the transaction on Alice's behalf using his own private key and thereby paying gas fee from his account and get the ERC20 fee: | ||
|
||
```bash | ||
cast send $ALICE_ADDRESS "execute(address,bytes,uint256,address,uint256,uint8,bytes32,bytes32)" $ERC20 $ERC20_TRANSFER_CALLDATA 0 $ERC20 1000 $V $R $S --private-key $BOB_PK --auth $SIGNED_AUTH | ||
``` | ||
|
||
- Bob will receive the ERC20 token as the fee, and Charles will receive the ERC20 token | ||
```bash | ||
cast call $ERC20 "balance(address)" $BOB_ADDRESS | ||
cast call $ERC20 "balance(address)" $CHARLES_ADDRESS | ||
``` | ||
|