-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: add Foundry era test node tutorial (#87)
- add tutorial for using era_test_node with Foundry to call swap router contract for Uniswap
- Loading branch information
Showing
2 changed files
with
319 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,290 @@ | ||
--- | ||
title: Interacting with Uniswap on a Fork of ZKsync Era Using era_test_node and Foundry | ||
description: A step-by-step guide to set up a fork of ZKsync Era mainnet and interact with the Uniswap contract to perform token swaps using Foundry. | ||
--- | ||
|
||
This tutorial shows you how to set up a local ZKsync Era environment using `era_test_node`, | ||
leverage Foundry's `cast` tool to interact with contracts, and perform token swaps on Uniswap. | ||
You'll learn how to: | ||
|
||
1. Set up your development environment. | ||
2. Streamline your workflow with environment variables. | ||
3. Check balances using `cast call`. | ||
4. Deposit ETH for WETH. | ||
5. Transfer WETH to another address. | ||
6. Swap WETH for an ERC20 token on Uniswap. | ||
|
||
Let's get started! | ||
|
||
--- | ||
|
||
## Prerequisites | ||
|
||
- **Foundry**: Ethereum development toolkit. [Installation Guide](https://book.getfoundry.sh/getting-started/installation) | ||
- **era_test_node**: In-memory node to fork ZKsync Era mainnet. [Installation Guide](https://docs.zksync.io/build/test-and-debug/in-memory-node#install-and-set-up-era_test_node) | ||
- **Online Converters**: | ||
- [ETH Converter](https://eth-converter.com/) for converting ETH values. | ||
- [Hex to Decimal Converter](https://www.rapidtables.com/convert/number/hex-to-decimal.html) for number conversions. | ||
- **ZKsync Era Explorer**: | ||
- [ZKsync Era Explorer](https://era.zksync.io/) to view contracts. | ||
- [Tokens List](https://explorer.zksync.io/tokens) to find token addresses. | ||
- **Uniswap Pool Explorer**: | ||
- [Uniswap Pools on ZKsync Era](https://app.uniswap.org/explore/pools/zksync) to find pools for swapping. | ||
- **Price Conversion Tool**: | ||
- [Kraken Convert](https://www.kraken.com/convert) to get current exchange rates. | ||
|
||
::callout{icon="i-heroicons-exclamation-triangle"} | ||
**Security Tip**: Never commit your private key to version control or share it publicly. | ||
Ensure your `.env` file is excluded from source control. `.env` should be used for | ||
development purposes only, see the [foundry keystore](https://docs.zksync.io/build/tooling/foundry/installation#private-key-setup-with-foundry-keystore) | ||
section of the docs for a more secure way to manage your private keys. | ||
|
||
:: | ||
|
||
--- | ||
|
||
## 1. Setting Up the Environment | ||
|
||
### Fork ZKsync Era Mainnet | ||
|
||
Start `era_test_node` and fork ZKsync Era mainnet: | ||
|
||
```bash | ||
era_test_node fork mainnet | ||
``` | ||
|
||
For debug mode (provides more transaction details and requires version `v0.1.0-alpha.27` or higher): | ||
|
||
```bash | ||
era_test_node -d fork mainnet | ||
``` | ||
|
||
--- | ||
|
||
## 2. Streamlining with Environment Variables | ||
|
||
Create a new project directory and a `.env` file in your project directory to store environment variables: | ||
|
||
```bash | ||
ETH_RPC_URL=http://localhost:8011 | ||
PRIVATE_KEY=rich_wallet_private_key_here | ||
ETH_GAS_LIMIT=10000000 # Optional | ||
WETH=0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91 | ||
MY_ADDRESS=address_of_the_rich_wallet | ||
SWAP_ROUTER=0x99c56385daBCE3E81d8499d0b8d0257aBC07E8A3 | ||
``` | ||
|
||
- **ETH_RPC_URL**: The URL of your local `era_test_node` instance. | ||
- **PRIVATE_KEY**: One of the private keys from the `era_test_node` accounts. | ||
- **ETH_GAS_LIMIT**: Optional gas limit for transactions. | ||
- **WETH**: [WETH token address on ZKsync Era](https://era.zksync.network/address/0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91). | ||
- **MY_ADDRESS**: The address of the rich wallet private key. | ||
- **SWAP_ROUTER**: [Uniswap Swap Router contract address on ZKsync Era](https://docs.uniswap.org/contracts/v3/reference/deployments/ZKsync-deployments). | ||
|
||
Run the following in your terminal in your project directory to load the environment variables: | ||
|
||
```bash | ||
source .env | ||
``` | ||
|
||
::callout{icon="i-heroicons-exclamation-triangle"} | ||
**Security Tip**: Never commit your private key to version control or share it publicly. | ||
Ensure your `.env` file is excluded from source control. `.env` should be used for | ||
development purposes only, see the [foundry keystore](https://docs.zksync.io/build/tooling/foundry/installation#private-key-setup-with-foundry-keystore) | ||
section of the docs for a more secure way to manage your private keys. | ||
:: | ||
|
||
--- | ||
|
||
## 3. Checking Balances | ||
|
||
To check the ETH balance of an address: | ||
|
||
```bash | ||
cast balance $MY_ADDRESS | ||
``` | ||
|
||
--- | ||
|
||
## 4. Depositing ETH for WETH | ||
|
||
Deposit ETH and receive WETH by interacting with the WETH contract: | ||
|
||
```bash | ||
cast send $WETH "deposit()" --value 500000000000000000 --private-key $PRIVATE_KEY | ||
``` | ||
|
||
- **Function**: `deposit()` | ||
- **Value**: Amount of ETH to deposit (in wei). For example, `0.5 ETH` is `500000000000000000 wei`. | ||
|
||
### Verify Your WETH Balance | ||
|
||
Check your WETH balance: | ||
|
||
```bash | ||
cast call $WETH "balanceOf(address)(uint256)" $MY_ADDRESS | ||
``` | ||
|
||
--- | ||
|
||
## 5. Transferring WETH | ||
|
||
To transfer WETH to another address: | ||
|
||
```bash | ||
cast send $WETH "transfer(address,uint256)" <RECIPIENT_ADDRESS> <AMOUNT_TO_TRANSFER> --private-key $PRIVATE_KEY | ||
``` | ||
|
||
- **`<RECIPIENT_ADDRESS>`**: The address to send WETH to. | ||
- **`<AMOUNT_TO_TRANSFER>`**: Amount of WETH to transfer (in wei). | ||
|
||
::callout{icon="i-heroicons-light-bulb"} | ||
**Check**: Use `cast call` to verify the transfer. | ||
:: | ||
|
||
--- | ||
|
||
## 6. Approving WETH for Swapping | ||
|
||
Before swapping WETH on Uniswap, approve the Swap Router to spend your WETH: | ||
|
||
```bash | ||
cast send $WETH "approve(address,uint256)" $SWAP_ROUTER <AMOUNT_TO_APPROVE> --private-key $PRIVATE_KEY | ||
``` | ||
|
||
- **`<AMOUNT_TO_APPROVE>`**: The amount of WETH you plan to swap (in wei). | ||
|
||
--- | ||
|
||
## 7. Selecting an ERC20 Token and Swap Pair | ||
|
||
### Find an ERC20 Token | ||
|
||
- Use the [ZKsync Era Tokens List](https://explorer.zksync.io/tokens) to choose an ERC20 token. | ||
- Set the ERC20 token address in your `.env` file: | ||
|
||
```bash | ||
ERC20=ERC20_TOKEN_ADDRESS_HERE | ||
``` | ||
|
||
### Find the Uniswap Pool and Fee Tier | ||
|
||
- Visit the [Uniswap Pools Explorer](https://app.uniswap.org/explore/pools/zksync) to find a pool for your token pair. | ||
- Note the **fee tier** of the pool (e.g., `3000` for 0.3% fee). | ||
- Set the fee in your `.env` file: | ||
|
||
```bash | ||
FEE=FEE_TIER_HERE | ||
``` | ||
|
||
--- | ||
|
||
## 8. Swapping WETH for an ERC20 Token | ||
|
||
We'll use the `exactInputSingle` function from the Uniswap Swap Router contract. This function accepts a struct as a parameter. | ||
|
||
### `exactInputSingle` Function Details | ||
|
||
```solidity | ||
function exactInputSingle(ExactInputSingleParams calldata params) | ||
external | ||
payable | ||
returns (uint256 amountOut); | ||
``` | ||
|
||
**Struct Definition:** | ||
|
||
```solidity | ||
struct ExactInputSingleParams { | ||
address tokenIn; // Token you're swapping from | ||
address tokenOut; // Token you're swapping to | ||
uint24 fee; // Fee tier of the pool | ||
address recipient; // Address to receive output tokens | ||
uint256 amountIn; // Amount of input tokens to swap | ||
uint256 amountOutMinimum; // Minimum amount of output tokens to receive | ||
uint160 sqrtPriceLimitX96; // Price limit for the swap (optional) | ||
} | ||
``` | ||
|
||
### Update the environment variables | ||
|
||
Your `.env` file should now have the following variables: | ||
|
||
```bash | ||
ETH_RPC_URL=http://localhost:8011 | ||
PRIVATE_KEY=rich_wallet_private_key_here | ||
ETH_GAS_LIMIT=10000000 # Optional | ||
WETH=0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91 | ||
MY_ADDRESS=your_wallet_address_here | ||
SWAP_ROUTER=0x99c56385daBCE3E81d8499d0b8d0257aBC07E8A3 | ||
ERC20=ERC20_TOKEN_ADDRESS_HERE | ||
FEE=FEE_TIER_HERE | ||
AMOUNT_IN=AMOUNT_OF_WETH_TO_SWAP_HERE | ||
AMOUNT_OUT_MIN=MINIMUM_AMOUNT_OF_ERC20_TO_RECEIVE_HERE | ||
SQRT_PRICE_LIMIT_X96=0 | ||
``` | ||
|
||
- **$AMOUNT_IN**: Amount of WETH to swap (in wei). | ||
- **$AMOUNT_OUT_MIN**: Minimum amount of ERC20 tokens to receive (in wei). Use [Kraken Convert](https://www.kraken.com/convert) to estimate this amount. | ||
- **$SQRT_PRICE_LIMIT_X96**: Set to `0` to accept current pool prices, or calculate based on desired price limit. | ||
|
||
Load the environment variables: | ||
|
||
```bash | ||
source .env | ||
``` | ||
|
||
### Execute the Swap Command | ||
|
||
```bash | ||
cast send $SWAP_ROUTER \ | ||
"exactInputSingle((address,address,uint24,address,uint256,uint256,uint160))" \ | ||
"($WETH,$ERC20,$FEE,$MY_ADDRESS,$AMOUNT_IN,$AMOUNT_OUT_MIN,$SQRT_PRICE_LIMIT_X96)" \ | ||
--value 0 --private-key $PRIVATE_KEY | ||
``` | ||
|
||
::callout{icon="i-heroicons-information-circle"} | ||
**Note**: If something goes wrong: | ||
|
||
- Check your `.env` file for mistakes | ||
- Reload the environment variables | ||
- Check the logs in the terminal where you started `era_test_node` | ||
:: | ||
|
||
--- | ||
|
||
## Notes and Tips | ||
|
||
- **EOA Restrictions**: Externally Owned Accounts can't interact directly with Uniswap V3 Pool | ||
contracts due to callback requirements. Use the Swap Router instead. | ||
- **Understanding Tuples**: When calling functions that accept structs, parameters are passed | ||
as tuples enclosed in parentheses and quotes to prevent shell parsing issues. | ||
- **Token Decimals**: Ensure you're using the correct number of decimals for your ERC20 token. | ||
- **Gas Limit**: If you encounter gas limit issues, set `ETH_GAS_LIMIT` in your `.env` file. | ||
- **Approvals**: Always approve the Swap Router to spend your tokens before swapping. | ||
- **Cast Send**: The `cast send` command is used to send transactions. `--value 0` is used | ||
because we are swapping WETH for an ERC20 token, and we are not sending any ETH in this | ||
transaction. | ||
- **AI**: Utilize tools like ChatGPT for quick calculations andexplanations. | ||
|
||
--- | ||
|
||
## Conclusion | ||
|
||
You've successfully set up a local ZKsync Era environment, interacted with contracts using `cast`, | ||
and performed a token swap on Uniswap. This setup allows you to test and develop smart contract | ||
interactions in a controlled environment. | ||
|
||
--- | ||
|
||
## Additional Resources | ||
|
||
- **ZKsync Era Documentation**: [ZKsync Docs](https://docs.zksync.io/) | ||
- **Foundry Book**: [Foundry Documentation](https://book.getfoundry.sh/) | ||
- **Uniswap V3 Documentation**: [Uniswap Docs](https://docs.uniswap.org/) | ||
- **ZKsync Era Explorer**: [ZKsync Era Explorer](https://era.zksync.network/) | ||
- **Uniswap Pools on ZKsync Era**: [Uniswap Pools](https://app.uniswap.org/explore/pools/zksync) | ||
|
||
--- | ||
|
||
**Happy coding and exploring ZKsync Era with Foundry and Cast!** |
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,29 @@ | ||
title: Interacting with Uniswap on a Fork of ZKsync Era Using era_test_node and Foundry | ||
featured: false | ||
authors: | ||
- name: bxpana | ||
url: https://github.com/bxpana | ||
avatar: https://avatars.githubusercontent.com/u/42230936?v=4 | ||
github_repo: https://github.com/ZKsync-Community-Hub | ||
tags: | ||
- zksync-era | ||
- foundry | ||
- cast | ||
- uniswap | ||
- tutorial | ||
summary: Set up a local ZKsync Era environment and perform token swaps using Foundry. | ||
description: | | ||
In this tutorial, you will set up a local ZKsync Era environment using era_test_node, leverage Foundry's cast tool to interact with contracts, and perform token swaps on Uniswap. You will learn how to check balances, deposit ETH for WETH, transfer WETH, and swap tokens using Uniswap's Swap Router. | ||
what_you_will_learn: | ||
- How to set up and fork a local ZKsync Era environment. | ||
- How to use environment variables to streamline your workflow. | ||
- How to check balances using cast. | ||
- How to deposit ETH for WETH and transfer WETH. | ||
- How to approve tokens and perform swaps on Uniswap using the Swap Router. | ||
updated: 2024-09-26 | ||
tools: | ||
- foundry | ||
- era_test_node | ||
- cast | ||
- uniswap | ||
- zksync-era-explorer |