SimpleSwap

1. Overview

SimpleSwap is an example project for swapping tokens using Uniswap V3.

The contract structure is broken down into individual contracts, each inherited from the previous contract. This was done as an exercise to better understand smart contract inheritance and to highlight reusable code sections.

ImportsVariablesGettersModifiersAndChecksUpdatesCoreSimpleSwap

This project builds on knowledge from the AavePM project.

The TokenSwap contract is a pure logic contract that can be used to swap ETH <-> USDC. It has no owner and is immutable. To upgrade the TokenSwap contract, a new contract must be deployed and the SimpleSwap contract must be updated to point to the new contract.

2. Usage

  1. Send ETH directly to the contract address to receive USDC on the sender address.
  2. Send USDC to the swapUsdc function to receive ETH on the sender address.

3. Functions

3.1. Swap Functions

FunctionRestrictionsDescription
receiveN/AReceive ETH and swap for USDC.
swapUsdcN/ASwap specified amount of USDC for ETH.

3.2. Withdraw Functions

FunctionRestrictionsDescription
withdrawEthOnly To Owner AddressWithdraw any ETH in the contract to an owner address.
withdrawTokensOnly To Owner AddressWithdraw specified token in the contract to an owner address.

3.3. Contract Upgrade Functions

FunctionRestrictionsDescription
upgradeContractOWNER_ROLEUpgrade the contract to the specified implementation.

3.4. Contract Update Functions

FunctionRestrictionsDescription
updateContractAddressOWNER_ROLEUpdate the specified contract address.
updateTokenAddressOWNER_ROLEUpdate the specified token address.
updateUniswapV3PoolOWNER_ROLEUpdate the specified Uniswap V3 pool.
updateSlippageToleranceOWNER_ROLEUpdate the specified slippage tolerance.

3.5. Role Management Functions

FunctionRestrictionsDescription
grantRoleRole AdminGrant a role to an address.
revokeRoleRole AdminRevoke a role from an address.
getRoleAdminN/AGet the admin of a role for the identifier.
getRoleMemberN/AGet the member of a role for the identifier.
getRoleMembersN/AGet the members of a role for the identifier.
getRoleMemberCountN/AGet the member count of a role for the identifier.
hasRoleN/ACheck if an address has a role.
renounceRoleRole MemberRenounce a role from an address.

3.6. Getter Functions

FunctionRestrictionsDescription
getCreatorN/AGet the creator address of the contract.
getEventBlockNumbersN/AGet the block numbers of all previous contract events.
getVersionN/AGet the current contract version.
getBalanceN/AGet the balance of the contract.
getContractAddressN/AGet the contract address of the identifier.
getTokenAddressN/AGet the token address of the identifier.
getUniswapV3PoolN/AGet the Uniswap V3 pool address of the identifier.
getSlippageToleranceN/AGet the slippage tolerance of the contract.
getSlippageToleranceMaximumN/AGet the maximum slippage tolerance of the contract.

4. Installation

4.1. Clone repository

git clone https://github.com/EridianAlpha/simple-swap.git

4.2. Install Dependencies

This should happen automatically when first running a command, but the installation can be manually triggered with the following commands:

git submodule init
git submodule update
make install

4.3. Create the .env file

Use the .env.example file as a template to create a .env file.

5. Testing

5.1. Tests (Fork)

make test
make test-v
make test-summary

5.2. Coverage (Fork)

make coverage
make coverage-report

6. Deployment

Deploys the SimpleSwap contract and all module contracts.

ChainCommand
Anvilmake deploy anvil
Holeskymake deploy holesky
Base Sepoliamake deploy base-sepolia
Base Mainnetmake deploy base-mainnet

7. Interactions

Interactions are defined in ./script/Interactions.s.sol

If DEPLOYED_CONTRACT_ADDRESS is set in the .env file, that contract address will be used for interactions. If that variable is not set, the latest deployment on the specified chain will be used.

Deployed Contracts:

7.1. Force Send ETH

Send ETH to the contract using a intermediate selfdestruct contract. This does not call the receive function on the contract. Input value in ETH e.g. 0.15.

ChainCommand
Anvilmake forceSendEth anvil
Holeskymake forceSendEth holesky
Base Sepoliamake forceSendEth base-sepolia
Base Mainnetmake forceSendEth base-mainnet

7.2. Swap Functions

7.2.1. Send ETH

Send ETH to the contract to receive USDC. Input value in ETH e.g. 0.15.

ChainCommand
Anvilmake sendEth anvil
Holeskymake sendEth holesky
Base Sepoliamake sendEth base-sepolia
Base Mainnetmake sendEth base-mainnet

7.2.2. Swap USDC

Swap USDC for ETH. Input value in USD e.g. 200.

ChainCommand
Anvilmake swapUsdc anvil
Holeskymake swapUsdc holesky
Base Sepoliamake swapUsdc base-sepolia
Base Mainnetmake swapUsdc base-mainnet

7.3. Withdraw Functions

7.3.1. Withdraw ETH

Withdraw all ETH in the contract to an owner address. Requires the specified withdrawal address to have the OWNER_ROLE. Input value as an address e.g. 0x123....

ChainCommand
Anvilmake withdrawEth anvil
Holeskymake withdrawEth holesky
Base Sepoliamake withdrawEth base-sepolia
Base Mainnetmake withdrawEth base-mainnet

7.3.2. Withdraw Tokens

Withdraw all of the specified token in the contract to an owner address. Requires the specified withdrawal address to have the OWNER_ROLE. Input value 1 as a token identifier e.g. USDC. Input value 2 as an address e.g. 0x123.... Combined input value e.g. USDC,0x123....

ChainCommand
Anvilmake withdrawTokens anvil
Holeskymake withdrawTokens holesky
Base Sepoliamake withdrawTokens base-sepolia
Base Mainnetmake withdrawTokens base-mainnet

7.4. Upgrades

Upgrade the contract to the latest logic implementation while maintaining the same proxy address. This also redeploys all modules and updates their contract addresses on SimpleSwap.

ChainCommand
Anvilmake upgrade anvil
Holeskymake upgrade holesky
Base Sepoliamake upgrade base-sepolia
Base Mainnetmake upgrade base-mainnet

7.5. Updates

7.5.1. Update Contract Address

Update the specified contract identifier to a new address. Input value 1 as a contract identifier e.g. uniswapV3Router. Input value 2 as an address e.g. 0x123.... Combined input value e.g. uniswapV3Router,0x123....

ChainCommand
Anvilmake updateContractAddress anvil
Holeskymake updateContractAddress holesky
Base Sepoliamake updateContractAddress base-sepolia
Base Mainnetmake updateContractAddress base-mainnet

7.5.2. Update Token Address

Update the specified token identifier to a new address. Input value 1 as a token identifier e.g. USDC. Input value 2 as an address e.g. 0x123.... Combined input value e.g. USDC,0x123....

ChainCommand
Anvilmake updateTokenAddress anvil
Holeskymake updateTokenAddress holesky
Base Sepoliamake updateTokenAddress base-sepolia
Base Mainnetmake updateTokenAddress base-mainnet

7.5.3. Update UniswapV3Pool Address

Update the specified token identifier to a new address and fee. Input value 1 as a UniswapV3Pool identifier e.g. USDC/ETH. Input value 2 as an address e.g. 0x123.... Input value 3 as a fee e.g. 500 for a fee of 0.05%. Combined input value e.g. USDC/ETH,0x123...,500.

ChainCommand
Anvilmake updateUniswapV3PoolAddress anvil
Holeskymake updateUniswapV3PoolAddress holesky
Base Sepoliamake updateUniswapV3PoolAddress base-sepolia
Base Mainnetmake updateUniswapV3PoolAddress base-mainnet

7.5.4. Update Slippage Tolerance

Input value to 2 decimal places e.g. 200 for a Slippage Tolerance of 0.5%.

ChainCommand
Anvilmake updateSlippageTolerance anvil
Holeskymake updateSlippageTolerance holesky
Base Sepoliamake updateSlippageTolerance base-sepolia
Base Mainnetmake updateSlippageTolerance base-mainnet

7.6. Role Management

7.6.1. Grant Role

Grant a role to an address. Requires the caller to be an admin of the role being granted. Input value 1 as a role e.g. OWNER_ROLE. Input value 2 as an address e.g. 0x123.... Combined input value e.g. OWNER_ROLE,0x123....

ChainCommand
Anvilmake grantRole anvil
Holeskymake grantRole holesky
Base Sepoliamake grantRole base-sepolia
Base Mainnetmake grantRole base-mainnet

7.6.2. Revoke Role

Revoke a role from an address. Requires the caller to be an admin of the role being revoked. Input value 1 as a role e.g. OWNER_ROLE. Input value 2 as an address e.g. 0x123.... Combined input value e.g. OWNER_ROLE,0x123....

ChainCommand
Anvilmake revokeRole anvil
Holeskymake revokeRole holesky
Base Sepoliamake revokeRole base-sepolia
Base Mainnetmake revokeRole base-mainnet

7.6.3. Get Role Admin

Get the admin for the specified role. Input value as a role e.g. OWNER_ROLE. Returns keccak256 hash of the admin role.

ChainCommand
Anvilmake getRoleAdmin anvil
Holeskymake getRoleAdmin holesky
Base Sepoliamake getRoleAdmin base-sepolia
Base Mainnetmake getRoleAdmin base-mainnet

7.6.4. Get Role Member

Get the member for the specified role and index. Input value 1 as a role e.g. OWNER_ROLE. Input value 2 as index e.g. 0. Combined input value e.g. OWNER_ROLE,0. Returns address of the member.

ChainCommand
Anvilmake getRoleMember anvil
Holeskymake getRoleMember holesky
Base Sepoliamake getRoleMember base-sepolia
Base Mainnetmake getRoleMember base-mainnet

7.6.5. Get Role Members

Get all members for the specified role. Input value as a role e.g. OWNER_ROLE. Returns array of addresses of the members.

ChainCommand
Anvilmake getRoleMembers anvil
Holeskymake getRoleMembers holesky
Base Sepoliamake getRoleMembers base-sepolia
Base Mainnetmake getRoleMembers base-mainnet

7.6.6. Get Role Member Count

Get the member count for the specified role. Input value as a role e.g. OWNER_ROLE. Returns the number of members in the role.

ChainCommand
Anvilmake getRoleMemberCount anvil
Holeskymake getRoleMemberCount holesky
Base Sepoliamake getRoleMemberCount base-sepolia
Base Mainnetmake getRoleMemberCount base-mainnet

7.6.7. Check Has Role

Check if an address has the specified role. Input value 1 as a role e.g. OWNER_ROLE. Input value 2 as an address e.g. 0x123.... Combined input value e.g. OWNER_ROLE,0x123....

ChainCommand
Anvilmake hasRole anvil
Holeskymake hasRole holesky
Base Sepoliamake hasRole base-sepolia
Base Mainnetmake hasRole base-mainnet

7.6.8. Renounce Role

Renounce the specified role from an address. Any address can renounce a role from themselves. Input value as a role e.g. OWNER_ROLE.

ChainCommand
Anvilmake renounceRole anvil
Holeskymake renounceRole holesky
Base Sepoliamake renounceRole base-sepolia
Base Mainnetmake renounceRole base-mainnet

7.7. Getters

7.7.1. Get Creator

Returns the contract creator address.

ChainCommand
Anvilmake getCreator anvil
Holeskymake getCreator holesky
Base Sepoliamake getCreator base-sepolia
Base Mainnetmake getCreator base-mainnet

7.7.2. Get Version

Returns the current version of the contract.

ChainCommand
Anvilmake getVersion anvil
Holeskymake getVersion holesky
Base Sepoliamake getVersion base-sepolia
Base Mainnetmake getVersion base-mainnet

7.7.3. Get Balance

Input value as token identifier e.g. USDC. Returns the balance of the contract for the specified token.

ChainCommand
Anvilmake getBalance anvil
Holeskymake getBalance holesky
Base Sepoliamake getBalance base-sepolia
Base Mainnetmake getBalance base-mainnet

7.7.4. Get Event Block Numbers

Returns the block numbers of all previous contract events.

ChainCommand
Anvilmake getEventBlockNumbers anvil
Holeskymake getEventBlockNumbers holesky
Base Sepoliamake getEventBlockNumbers base-sepolia
Base Mainnetmake getEventBlockNumbers base-mainnet

7.7.5. Get Contract Address

Input value as a contract identifier e.g. uniswapV3Router. Returns the contract address of the specified identifier.

ChainCommand
Anvilmake getContractAddress anvil
Holeskymake getContractAddress holesky
Base Sepoliamake getContractAddress base-sepolia
Base Mainnetmake getContractAddress base-mainnet

7.7.6. Get Token Address

Input value as a token identifier e.g. USDC. Returns the token address of the specified identifier.

ChainCommand
Anvilmake getTokenAddress anvil
Holeskymake getTokenAddress holesky
Base Sepoliamake getTokenAddress base-sepolia
Base Mainnetmake getTokenAddress base-mainnet

7.7.7. Get Uniswap V3 Pool

Input value as a Uniswap V3 Pool identifier e.g. USDC/ETH. Returns the address and fee of the specified identifier.

ChainCommand
Anvilmake getUniswapV3Pool anvil
Holeskymake getUniswapV3Pool holesky
Base Sepoliamake getUniswapV3Pool base-sepolia
Base Mainnetmake getUniswapV3Pool base-mainnet

7.7.8. Get Module Version

Input value as a module identifier e.g. tokenSwapCalcsModule. Returns the version of the specified module.

ChainCommand
Anvilmake getModuleVersion anvil
Holeskymake getModuleVersion holesky
Base Sepoliamake getModuleVersion base-sepolia
Base Mainnetmake getModuleVersion base-mainnet

8. Build and Deploy Documentation

Instructions on how to build and deploy the documentation book are detailed here: https://docs.eridianalpha.com/ethereum-dev/foundry-notes/docs-and-github-pages

9. License

MIT