diff --git a/.github/workflows/foundry.yml b/.github/workflows/foundry.yml index c354248..8c92063 100644 --- a/.github/workflows/foundry.yml +++ b/.github/workflows/foundry.yml @@ -29,14 +29,9 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1.2.0 - - name: Run Forge build - run: | - forge --version - forge build --sizes - id: build - - name: Run Forge tests run: | + forge clean forge test -vvv id: test - name: Generate coverage report diff --git a/.gitignore b/.gitignore index c2c323d..8c47cb1 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,14 @@ node_modules # misc .DS_Store + +# .env files .env +.env.local +.env.mainnet +.env.sepolia +.env.arbitrum-one +.env.arbitrum-sepolia # logs npm-debug.log* diff --git a/foundry/.env.arbitrum-one.example b/foundry/.env.arbitrum-one.example new file mode 100644 index 0000000..10b3c58 --- /dev/null +++ b/foundry/.env.arbitrum-one.example @@ -0,0 +1,5 @@ +ETHERSCAN_API_KEY="" # this must be an arbiscan api key +FOX_TOKEN_ADDRESS="" +PRIVATE_KEY="" +RPC_URL="https://arbitrum-mainnet.infura.io/v3/" +VERIFIER_URL="https://api.arbiscan.io/api" diff --git a/foundry/.env.arbitrum-sepolia.example b/foundry/.env.arbitrum-sepolia.example new file mode 100644 index 0000000..56ce63a --- /dev/null +++ b/foundry/.env.arbitrum-sepolia.example @@ -0,0 +1,5 @@ +ETHERSCAN_API_KEY="" # this must be an arbiscan api key +FOX_TOKEN_ADDRESS="" +PRIVATE_KEY="" +RPC_URL="https://arbitrum-sepolia.infura.io/v3/" +VERIFIER_URL="https://api-sepolia.arbiscan.io/api" diff --git a/foundry/.env.local.example b/foundry/.env.local.example new file mode 100644 index 0000000..1b38de9 --- /dev/null +++ b/foundry/.env.local.example @@ -0,0 +1,5 @@ +ETHERSCAN_API_KEY="not-required" +FOX_TOKEN_ADDRESS="0xc770EEfAd204B5180dF6a14Ee197D99d808ee52d" +PRIVATE_KEY="" +RPC_URL="http://localhost:8545" +VERIFIER_URL="not-required" diff --git a/foundry/.env.mainnet.example b/foundry/.env.mainnet.example new file mode 100644 index 0000000..88e5af4 --- /dev/null +++ b/foundry/.env.mainnet.example @@ -0,0 +1,5 @@ +ETHERSCAN_API_KEY="" +FOX_TOKEN_ADDRESS="0xc770EEfAd204B5180dF6a14Ee197D99d808ee52d" +PRIVATE_KEY="" +RPC_URL="https://mainnet.infura.io/v3/" +VERIFIER_URL="https://api.etherscan.io/api" diff --git a/foundry/.env.sepolia.example b/foundry/.env.sepolia.example new file mode 100644 index 0000000..d139d8a --- /dev/null +++ b/foundry/.env.sepolia.example @@ -0,0 +1,5 @@ +ETHERSCAN_API_KEY="" +FOX_TOKEN_ADDRESS="" +PRIVATE_KEY="" +RPC_URL="https://sepolia.infura.io/v3/" +VERIFIER_URL="https://api-sepolia.etherscan.io/api" diff --git a/foundry/.gitignore b/foundry/.gitignore index 85198aa..df22b84 100644 --- a/foundry/.gitignore +++ b/foundry/.gitignore @@ -2,10 +2,8 @@ cache/ out/ -# Ignores development broadcast logs -!/broadcast -/broadcast/*/31337/ -/broadcast/**/dry-run/ +# Ignores broadcast logs +/broadcast # Docs docs/ diff --git a/foundry/README.md b/foundry/README.md index 9265b45..e8d33d0 100644 --- a/foundry/README.md +++ b/foundry/README.md @@ -1,66 +1,74 @@ -## Foundry +# rFOX -**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** +## Setup -Foundry consists of: +1. Install foundry https://book.getfoundry.sh/getting-started/installation +2. Install slither `brew install slither-analyzer` +3. Set up .env files: + 1. Copy an example .env file for your chosen environment: + ```shell + cp .env..example .env. + ``` + 2. Fill in the needed env vars there -- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). -- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. -- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. -- **Chisel**: Fast, utilitarian, and verbose solidity REPL. +### Private key -## Documentation - -https://book.getfoundry.sh/ - -## Usage - -### Build +To derive a private key 0 from your mnemonic: ```shell -$ forge build +cast wallet derive-private-key 0 ``` -### Test +### Faucets -```shell -$ forge test -``` +**Arbitrum sepolia** -### Format +Grab some ETH: +https://www.l2faucet.com/arbitrum -```shell -$ forge fmt -``` +Grab some USDC: +https://faucet.circle.com/ -### Gas Snapshots +## Deploying -```shell -$ forge snapshot -``` +### Local deployment +Deploying locally is different to deploying directly to a network for 2 reasons: +1. Its being deployed to a fork of another network (typically ethereum mainnet) rather than a real network. +2. There's no local instance of etherscan, so etherscan verification is skipped. -### Anvil +When deploying locally, it's necessary to start a local fork before deploying: ```shell -$ anvil +# RPC_URL for your env can be accessible with `source .env. --private-key -``` +cd foundry -### Cast +# Install +forge install -```shell -$ cast +# Deploy +./deploy.sh --env + +# Test the proxy contract has been deployed with the correct version. +# RPC_URL for your env can be accessible with `source .env. "version()(uint256)" --rpc-url $RPC_URL ``` -### Help +### Manually verifying + +If contract verification fails for any reason, you can verify without re-deploying: ```shell -$ forge --help -$ anvil --help -$ cast --help +forge verify-contract \ + --verifier etherscan \ + --verifier-url https://api-sepolia.arbiscan.io/api \ + --compiler-version "v0.8.25" \ + --etherscan-api-key $ARBISCAN_API_KEY \ + $CONTRACT_IMPLEMENTATION_ADDRESS \ + src/FoxStakingV1.sol:FoxStakingV1 ``` diff --git a/foundry/deploy.sh b/foundry/deploy.sh new file mode 100755 index 0000000..c8cdeac --- /dev/null +++ b/foundry/deploy.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +# Load generic environment variables +source .env + +# Parse command line options +while [[ "$#" -gt 0 ]]; do + case $1 in + --env) ENVIRONMENT="$2"; shift ;; # Shift past the value + *) echo "Unknown parameter passed: $1"; exit 1 ;; + esac + shift # Shift past the current key or value +done + +# Path to the environment file +ENV_FILE=".env.$ENVIRONMENT" + +# Check if the environment file exists +if [ ! -f "$ENV_FILE" ]; then + echo "Environment file $ENV_FILE does not exist." + exit 1 +fi + +# Source environment variables +source $ENV_FILE + +# Echo back the environment for verification +echo "Deploying to $ENVIRONMENT environment with settings from '$ENV_FILE'..." + +# Deployment-related commands +echo "Running deployment tasks..." + +# Clean the build directory +forge clean + +if [ "$ENVIRONMENT" = "local" ]; then + # Local environment-specific commands (no etherscan verification) + FOX_TOKEN_ADDRESS="$FOX_TOKEN_ADDRESS" forge script script/DeployFoxStaking.s.sol:DeployFoxStaking \ + --fork-url $RPC_URL \ + --private-key $PRIVATE_KEY \ + --broadcast \ + -vvvvv +else + # All other environments use etherscan verification (which automatically reroutes to arbiscan as needed) + FOX_TOKEN_ADDRESS="$FOX_TOKEN_ADDRESS" forge script script/DeployFoxStaking.s.sol:DeployFoxStaking \ + --rpc-url $RPC_URL \ + --private-key $PRIVATE_KEY \ + --broadcast \ + -vvvvv \ + --verify \ + --verifier etherscan \ + --verifier-url $VERIFIER_URL \ + --etherscan-api-key $ETHERSCAN_API_KEY +fi diff --git a/foundry/script/DeployFoxStaking.s.sol b/foundry/script/DeployFoxStaking.s.sol new file mode 100644 index 0000000..592d774 --- /dev/null +++ b/foundry/script/DeployFoxStaking.s.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.25; + +import "forge-std/Script.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; +import {FoxStakingV1} from "../src/FoxStakingV1.sol"; + +contract DeployFoxStaking is Script { + address foxTokenAddress; + + function setUp() public { + foxTokenAddress = vm.envAddress("FOX_TOKEN_ADDRESS"); + } + + function run() public { + vm.startBroadcast(); + address foxStakingProxy = Upgrades.deployUUPSProxy( + "FoxStakingV1.sol", + abi.encodeCall(FoxStakingV1.initialize, (foxTokenAddress)) + ); + vm.stopBroadcast(); + console.log("Contract deployed at:", foxStakingProxy); + } +}