Awesome
๐ช ERC20 Multi Meta Transaction
โ๏ธ Architecture
A meta transaction is a method that allows users to interact with the Ethereum network without requiring ETH for gas fees. In this setup, a user signs a transaction with their private key, while a third-party (a relayer) submits this transaction to the network and pays the gas fees. The transaction data is typically structured and signed according to the EIP-712 standard, enhancing security and readability. Furthermore, the ERC20 Permit method allows the token owner to delegate gas payments to another party, supporting the execution of the meta transaction. This process enhances the accessibility and usability of the Ethereum network, especially for users who do not hold ETH. To provide a better understanding, we've included a screenshot of the architecture.
EIP-712 - Typed Structured Data Hashing and Signing
EIP-712 is an Ethereum Improvement Proposal that provides a standard for structuring and displaying data in a human-readable format during Ethereum transactions. It improves the usability of signed data structures, making it easier for users to understand what they're signing.
ERC-2612 - Permit Extension for EIP-20 Signed Approvals
ERC-20 permit, also known as EIP-2612, is a method used in Ethereum blockchain that allows holders of ERC-20 tokens to approve the spending of their tokens via a signature, instead of requiring an Ethereum transaction.
Forwarder Contract
The smart contract Forwarder.sol
extends the EIP712 and entails the following core functions.
verify
: Verifies the signature based on the typed structured data.
function verify(MetaTx calldata _tx, bytes calldata _signature) internal view returns (bool) {
address signer = _hashTypedDataV4(
keccak256(abi.encode(_TYPEHASH, _tx.from, _tx.to, _tx.token, _tx.amount, _tx.nonce))
).recover(_signature);
return signer == _tx.from && nonces[_tx.from] == _tx.nonce;
}
batchTransfer
Executes meta transactions in batches. If one transaction is invalid or has a malformed signature, the process continues as long as the remaining transactions are valid.
function batchTransfer(MetaTxWithSig[] calldata _metaTxWithSig, uint256 gas) external onlyOwner {
uint256 transactionsLength = _metaTxWithSig.length;
for (uint256 i = 0; i < transactionsLength; ++i) {
bytes calldata signature = _metaTxWithSig[i].signature;
MetaTx calldata metaTx = _metaTxWithSig[i].metaTx;
if (verify(metaTx, signature) && whitelistedTokens[IERC20(metaTx.token)] && metaTx.to != address(0)) {
nonces[metaTx.from] = nonces[metaTx.from] + 1;
metaTx.token.transferFrom(metaTx.from, metaTx.to, metaTx.amount);
}
require(gasleft() > gas / 63, "Not enough gas");
}
}
ERC20 Permit token
Here's the sample implementation of ERC20 Permit
contract Token1 is ERC20, ERC20Permit {
constructor() ERC20("Token1", "T1") ERC20Permit("Token1") {
_mint(msg.sender, 1000000 * 10 ** 18);
}
}
โก๏ธ Demo
๐ป Development
For this project to function, Redis must be installed, serving as our database to store transaction data.
After you clone the repository, change your directory to the project's directory and run the following commands:
1. Copy the environment variables from frontend
and relayer
workspaces
cp frontend/.env.development frontend/.env
cp relayer/.env.development relayer/.env
2. Install npm modules
yarn install
3. Start the Redis server. In your terminal simply run:
redis-server
4. Start Hardhat local node server. Open a new tab in your terminal, change directory to the contracts
folder and simply run:
npx hardhat node
5. Deploy the smart contracts. Open a new tab in your terminal, simply run:
yarn contracts:deploy:localhost
It should display something like this in your terminal.
Forwarder deployed to: 0x...123
Token1 whitelisted and deployed to 0x...123
Token2 whitelisted and deployed to 0x...123
Token3 whitelisted and deployed to 0x...123
6. Setup the environment variables
In the relayer workspace, copy and paste the deployed forwarder
contract address, along with the private key of Hardhat's deployer, typically designated as account index 0.
FORWARDER_LOCALHOST_ADDRESS=
PRIV_KEY=
In the frontend
workspace, copy and paste as well the forwarder
contract address.
NEXT_PUBLIC_FORWARDER_LOCALHOST_ADDRESS
Same in the frontend
workspace, copy and paste the tokens addresses in the blockchain.ts
file under the utils
folder.
token: {
tokens: [
{
address: {
localhost: '0x...',
sepolia: '',
goerli: ''
}
},
{
address: {
localhost: '0x..',
sepolia: '',
goerli: ''
}
},
{
address: {
localhost: '0x..',
sepolia: '',
goerli: ''
}
}
],
}
7. Open a new tab in your terminal and run the relayer
server
yarn relayer:dev
8. Open a new tab in your terminal and run the frontend
yarn frontend:dev
9. In your browser, navigate to localhost:3000
, and that's it you successfully run the project. Have fun! ๐
๐งช Unit Tests
We utilize Hardhat given its robust capabilities for testing smart contracts, encompassing Solidity debugging and clear error messages.
To run the test suite. Simply run:
yarn contracts:test
Here's the sample report from the contract's test suite
ยท--------------------------------|----------------------------|-------------|-----------------------------ยท
| Solc version: 0.8.17 ยท Optimizer enabled: false ยท Runs: 200 ยท Block limit: 30000000 gas โ
ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท
| Methods โ
ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
| Contract ยท Method ยท Min ยท Max ยท Avg ยท # calls ยท usd (avg) โ
ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
| Forwarder ยท batchTransfer ยท - ยท - ยท 547434 ยท 1 ยท - โ
ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
| Forwarder ยท whitelistToken ยท - ยท - ยท 46764 ยท 3 ยท - โ
ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
| Token1 ยท permit ยท 77091 ยท 77103 ยท 77099 ยท 3 ยท - โ
ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
| Token1 ยท transfer ยท 52143 ยท 52155 ยท 52151 ยท 3 ยท - โ
ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
| Token2 ยท permit ยท 77067 ยท 77103 ยท 77087 ยท 3 ยท - โ
ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
| Token2 ยท transfer ยท 52143 ยท 52155 ยท 52151 ยท 3 ยท - โ
ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
| Token3 ยท permit ยท 77091 ยท 77103 ยท 77095 ยท 3 ยท - โ
ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
| Token3 ยท transfer ยท 52143 ยท 52155 ยท 52151 ยท 3 ยท - โ
ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
| Deployments ยท ยท % of limit ยท โ
ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
| Forwarder ยท - ยท - ยท 1924066 ยท 6.4 % ยท - โ
ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
| Token1 ยท - ยท - ยท 2269191 ยท 7.6 % ยท - โ
ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
| Token2 ยท - ยท - ยท 2269191 ยท 7.6 % ยท - โ
ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยทยท|ยทยทยทยทยทยทยทยทยทยทยทยทยทยท
| Token3 ยท - ยท - ยท 2269191 ยท 7.6 % ยท - โ
ยท--------------------------------|--------------|-------------|-------------|---------------|-------------ยท
๐ License
This project is licensed under GNU General Public License v3.
๐ Thank you!
If this project interests you, follow me on Twitter