diff --git a/foundry/.env.arbitrum-one.example b/foundry/.env.arbitrum-one.example index 10b3c58..e85a17b 100644 --- a/foundry/.env.arbitrum-one.example +++ b/foundry/.env.arbitrum-one.example @@ -1,5 +1,5 @@ ETHERSCAN_API_KEY="" # this must be an arbiscan api key -FOX_TOKEN_ADDRESS="" +STAKING_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 index 56ce63a..ca5b0d8 100644 --- a/foundry/.env.arbitrum-sepolia.example +++ b/foundry/.env.arbitrum-sepolia.example @@ -1,5 +1,5 @@ ETHERSCAN_API_KEY="" # this must be an arbiscan api key -FOX_TOKEN_ADDRESS="" +STAKING_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 index 1b38de9..77dc980 100644 --- a/foundry/.env.local.example +++ b/foundry/.env.local.example @@ -1,5 +1,5 @@ ETHERSCAN_API_KEY="not-required" -FOX_TOKEN_ADDRESS="0xc770EEfAd204B5180dF6a14Ee197D99d808ee52d" +STAKING_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 index 88e5af4..81f32ee 100644 --- a/foundry/.env.mainnet.example +++ b/foundry/.env.mainnet.example @@ -1,5 +1,5 @@ ETHERSCAN_API_KEY="" -FOX_TOKEN_ADDRESS="0xc770EEfAd204B5180dF6a14Ee197D99d808ee52d" +STAKING_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 index d139d8a..9f192c6 100644 --- a/foundry/.env.sepolia.example +++ b/foundry/.env.sepolia.example @@ -1,5 +1,5 @@ ETHERSCAN_API_KEY="" -FOX_TOKEN_ADDRESS="" +STAKING_TOKEN_ADDRESS="" PRIVATE_KEY="" RPC_URL="https://sepolia.infura.io/v3/" VERIFIER_URL="https://api-sepolia.etherscan.io/api" diff --git a/foundry/README.md b/foundry/README.md index e8d33d0..7766224 100644 --- a/foundry/README.md +++ b/foundry/README.md @@ -5,11 +5,11 @@ 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 + 1. Copy an example .env file for your chosen environment: + ```shell + cp .env..example .env. + ``` + 2. Fill in the needed env vars there ### Private key @@ -32,7 +32,9 @@ https://faucet.circle.com/ ## Deploying ### 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. @@ -46,7 +48,7 @@ anvil --rpc-url $RPC_URL ### Deployment steps ```shell -cd foundry +cd foundry # Install forge install @@ -70,5 +72,5 @@ forge verify-contract \ --compiler-version "v0.8.25" \ --etherscan-api-key $ARBISCAN_API_KEY \ $CONTRACT_IMPLEMENTATION_ADDRESS \ - src/FoxStakingV1.sol:FoxStakingV1 + src/StakingV1.sol:StakingV1 ``` diff --git a/foundry/deploy.sh b/foundry/deploy.sh index c8cdeac..d4dfb5a 100755 --- a/foundry/deploy.sh +++ b/foundry/deploy.sh @@ -35,14 +35,14 @@ 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 \ + STAKING_TOKEN_ADDRESS="$STAKING_TOKEN_ADDRESS" forge script script/DeployStaking.s.sol:DeployStaking \ --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 \ + STAKING_TOKEN_ADDRESS="$STAKING_TOKEN_ADDRESS" forge script script/DeployStaking.s.sol:DeployStaking \ --rpc-url $RPC_URL \ --private-key $PRIVATE_KEY \ --broadcast \ diff --git a/foundry/script/DeployFoxStaking.s.sol b/foundry/script/DeployStaking.s.sol similarity index 62% rename from foundry/script/DeployFoxStaking.s.sol rename to foundry/script/DeployStaking.s.sol index caa6c82..79e9864 100644 --- a/foundry/script/DeployFoxStaking.s.sol +++ b/foundry/script/DeployStaking.s.sol @@ -7,19 +7,19 @@ import {Upgrades} from "openzeppelin-foundry-upgrades/Upgrades.sol"; import {StakingV1} from "../src/StakingV1.sol"; contract DeployStaking is Script { - address foxTokenAddress; + address stakingTokenAddress; function setUp() public { - foxTokenAddress = vm.envAddress("FOX_TOKEN_ADDRESS"); + stakingTokenAddress = vm.envAddress("STAKING_TOKEN_ADDRESS"); } function run() public { vm.startBroadcast(); - address foxStakingProxy = Upgrades.deployUUPSProxy( + address stakingProxy = Upgrades.deployUUPSProxy( "StakingV1.sol", - abi.encodeCall(StakingV1.initialize, (foxTokenAddress)) + abi.encodeCall(StakingV1.initialize, (stakingTokenAddress)) ); vm.stopBroadcast(); - console.log("Contract deployed at:", foxStakingProxy); + console.log("Contract deployed at:", stakingProxy); } } diff --git a/foundry/src/StakingV1.sol b/foundry/src/StakingV1.sol index b7f9632..42e0dc2 100644 --- a/foundry/src/StakingV1.sol +++ b/foundry/src/StakingV1.sol @@ -158,10 +158,10 @@ contract StakingV1 is info.earnedRewards; } - /// @notice Allows a user to stake a specified amount of FOX tokens and assign a RUNE address for rewards - which can be changed later on. + /// @notice Allows a user to stake a specified amount of staking tokens and assign a RUNE address for rewards - which can be changed later on. /// This has to be initiated by the user itself i.e msg.sender only, cannot be called by an address for another - /// @param amount The amount of FOX tokens to be staked. - /// @param runeAddress The RUNE address to be associated with the user's staked FOX position. + /// @param amount The amount of staking tokens to be staked. + /// @param runeAddress The RUNE address to be associated with the user's staked staking position. function stake( uint256 amount, string memory runeAddress @@ -170,7 +170,7 @@ contract StakingV1 is bytes(runeAddress).length == 43, "Rune address must be 43 characters" ); - require(amount > 0, "FOX amount to stake must be greater than 0"); + require(amount > 0, "amount to stake must be greater than 0"); updateReward(msg.sender); stakingToken.safeTransferFrom(msg.sender, address(this), amount); @@ -182,9 +182,9 @@ contract StakingV1 is emit Stake(msg.sender, amount, runeAddress); } - /// @notice Initiates the unstake process for a specified amount of FOX, starting the cooldown period (28 days). + /// @notice Initiates the unstake process for a specified amount of staking token, starting the cooldown period (28 days). /// This has to be initiated by the user itself i.e msg.sender only, cannot be called by an address for another - /// @param amount The amount of FOX tokens to be unstaked. + /// @param amount The amount of staking tokens to be unstaked. function unstake( uint256 amount ) external whenNotPaused whenUnstakingNotPaused nonReentrant { @@ -287,7 +287,7 @@ contract StakingV1 is /// @notice Allows a user to initially set (or update) their THORChain (RUNE) address for receiving staking rewards. /// This has to be initiated by the user itself i.e msg.sender only, cannot be called by an address for another - /// @param runeAddress The new RUNE address to be associated with the user's staked FOX position. + /// @param runeAddress The new RUNE address to be associated with the user's staked position. function setRuneAddress(string memory runeAddress) external { require( bytes(runeAddress).length == 43, @@ -299,10 +299,10 @@ contract StakingV1 is emit SetRuneAddress(msg.sender, oldRuneAddress, runeAddress); } - /// @notice View the staked balance of FOX tokens for a given address. + /// @notice View the staked balance of tokens for a given address. /// This can be initiated by any address with any address as param, as this has view modifier i.e everything is public on-chain - /// @param account The address we're getting the staked FOX balance for. - /// @return total The total amount of FOX tokens held. + /// @param account The address we're getting the staked staking balance for. + /// @return total The total amount of staking tokens held. function balanceOf(address account) external view returns (uint256 total) { StakingInfo memory info = stakingInfo[account]; return info.stakingBalance + info.unstakingBalance; diff --git a/foundry/test/StakingTestStaking.t.sol b/foundry/test/StakingTestStaking.t.sol index bb4ee4e..f8f0dfa 100644 --- a/foundry/test/StakingTestStaking.t.sol +++ b/foundry/test/StakingTestStaking.t.sol @@ -154,7 +154,7 @@ contract FOXStakingTestStaking is Test { vm.assertEq(unstakingBalance, 0); // Try to stake 0 - vm.expectRevert("FOX amount to stake must be greater than 0"); + vm.expectRevert("amount to stake must be greater than 0"); foxStaking.stake(0, runeAddress); // Check user staking balances are unchanged diff --git a/scripts/rewards-distribution/generated/abi-types.ts b/scripts/rewards-distribution/generated/abi-types.ts index 6ee8d51..4c60da9 100644 --- a/scripts/rewards-distribution/generated/abi-types.ts +++ b/scripts/rewards-distribution/generated/abi-types.ts @@ -1,488 +1,3 @@ -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// FoxStakingV1 -////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -export const foxStakingV1Abi = [ - { type: "constructor", inputs: [], stateMutability: "nonpayable" }, - { - type: "function", - inputs: [], - name: "UPGRADE_INTERFACE_VERSION", - outputs: [{ name: "", internalType: "string", type: "string" }], - stateMutability: "view", - }, - { - type: "function", - inputs: [{ name: "account", internalType: "address", type: "address" }], - name: "balanceOf", - outputs: [{ name: "total", internalType: "uint256", type: "uint256" }], - stateMutability: "view", - }, - { - type: "function", - inputs: [], - name: "cooldownPeriod", - outputs: [{ name: "", internalType: "uint256", type: "uint256" }], - stateMutability: "view", - }, - { - type: "function", - inputs: [], - name: "foxToken", - outputs: [{ name: "", internalType: "contract IERC20", type: "address" }], - stateMutability: "view", - }, - { - type: "function", - inputs: [ - { name: "account", internalType: "address", type: "address" }, - { name: "index", internalType: "uint256", type: "uint256" }, - ], - name: "getUnstakingInfo", - outputs: [ - { - name: "", - internalType: "struct UnstakingInfo", - type: "tuple", - components: [ - { - name: "unstakingBalance", - internalType: "uint256", - type: "uint256", - }, - { name: "cooldownExpiry", internalType: "uint256", type: "uint256" }, - ], - }, - ], - stateMutability: "view", - }, - { - type: "function", - inputs: [{ name: "account", internalType: "address", type: "address" }], - name: "getUnstakingInfoCount", - outputs: [{ name: "", internalType: "uint256", type: "uint256" }], - stateMutability: "view", - }, - { - type: "function", - inputs: [ - { name: "foxTokenAddress", internalType: "address", type: "address" }, - ], - name: "initialize", - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - inputs: [], - name: "owner", - outputs: [{ name: "", internalType: "address", type: "address" }], - stateMutability: "view", - }, - { - type: "function", - inputs: [], - name: "pause", - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - inputs: [], - name: "pauseStaking", - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - inputs: [], - name: "pauseUnstaking", - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - inputs: [], - name: "pauseWithdrawals", - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - inputs: [], - name: "paused", - outputs: [{ name: "", internalType: "bool", type: "bool" }], - stateMutability: "view", - }, - { - type: "function", - inputs: [], - name: "proxiableUUID", - outputs: [{ name: "", internalType: "bytes32", type: "bytes32" }], - stateMutability: "view", - }, - { - type: "function", - inputs: [], - name: "renounceOwnership", - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - inputs: [ - { name: "newCooldownPeriod", internalType: "uint256", type: "uint256" }, - ], - name: "setCooldownPeriod", - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - inputs: [{ name: "runeAddress", internalType: "string", type: "string" }], - name: "setRuneAddress", - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - inputs: [ - { name: "amount", internalType: "uint256", type: "uint256" }, - { name: "runeAddress", internalType: "string", type: "string" }, - ], - name: "stake", - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - inputs: [{ name: "", internalType: "address", type: "address" }], - name: "stakingInfo", - outputs: [ - { name: "stakingBalance", internalType: "uint256", type: "uint256" }, - { name: "unstakingBalance", internalType: "uint256", type: "uint256" }, - { name: "runeAddress", internalType: "string", type: "string" }, - ], - stateMutability: "view", - }, - { - type: "function", - inputs: [], - name: "stakingPaused", - outputs: [{ name: "", internalType: "bool", type: "bool" }], - stateMutability: "view", - }, - { - type: "function", - inputs: [{ name: "newOwner", internalType: "address", type: "address" }], - name: "transferOwnership", - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - inputs: [], - name: "unpause", - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - inputs: [], - name: "unpauseStaking", - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - inputs: [], - name: "unpauseUnstaking", - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - inputs: [], - name: "unpauseWithdrawals", - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - inputs: [{ name: "amount", internalType: "uint256", type: "uint256" }], - name: "unstake", - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - inputs: [], - name: "unstakingPaused", - outputs: [{ name: "", internalType: "bool", type: "bool" }], - stateMutability: "view", - }, - { - type: "function", - inputs: [ - { name: "newImplementation", internalType: "address", type: "address" }, - { name: "data", internalType: "bytes", type: "bytes" }, - ], - name: "upgradeToAndCall", - outputs: [], - stateMutability: "payable", - }, - { - type: "function", - inputs: [], - name: "version", - outputs: [{ name: "", internalType: "uint256", type: "uint256" }], - stateMutability: "view", - }, - { - type: "function", - inputs: [{ name: "index", internalType: "uint256", type: "uint256" }], - name: "withdraw", - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - inputs: [], - name: "withdraw", - outputs: [], - stateMutability: "nonpayable", - }, - { - type: "function", - inputs: [], - name: "withdrawalsPaused", - outputs: [{ name: "", internalType: "bool", type: "bool" }], - stateMutability: "view", - }, - { - type: "event", - anonymous: false, - inputs: [ - { - name: "version", - internalType: "uint64", - type: "uint64", - indexed: false, - }, - ], - name: "Initialized", - }, - { - type: "event", - anonymous: false, - inputs: [ - { - name: "previousOwner", - internalType: "address", - type: "address", - indexed: true, - }, - { - name: "newOwner", - internalType: "address", - type: "address", - indexed: true, - }, - ], - name: "OwnershipTransferred", - }, - { - type: "event", - anonymous: false, - inputs: [ - { - name: "account", - internalType: "address", - type: "address", - indexed: false, - }, - ], - name: "Paused", - }, - { - type: "event", - anonymous: false, - inputs: [ - { - name: "account", - internalType: "address", - type: "address", - indexed: true, - }, - { - name: "oldRuneAddress", - internalType: "string", - type: "string", - indexed: true, - }, - { - name: "newRuneAddress", - internalType: "string", - type: "string", - indexed: true, - }, - ], - name: "SetRuneAddress", - }, - { - type: "event", - anonymous: false, - inputs: [ - { - name: "account", - internalType: "address", - type: "address", - indexed: true, - }, - { - name: "amount", - internalType: "uint256", - type: "uint256", - indexed: false, - }, - { - name: "runeAddress", - internalType: "string", - type: "string", - indexed: true, - }, - ], - name: "Stake", - }, - { - type: "event", - anonymous: false, - inputs: [ - { - name: "account", - internalType: "address", - type: "address", - indexed: false, - }, - ], - name: "Unpaused", - }, - { - type: "event", - anonymous: false, - inputs: [ - { - name: "account", - internalType: "address", - type: "address", - indexed: true, - }, - { - name: "amount", - internalType: "uint256", - type: "uint256", - indexed: false, - }, - { - name: "cooldownExpiry", - internalType: "uint256", - type: "uint256", - indexed: false, - }, - ], - name: "Unstake", - }, - { - type: "event", - anonymous: false, - inputs: [ - { - name: "newCooldownPeriod", - internalType: "uint256", - type: "uint256", - indexed: false, - }, - ], - name: "UpdateCooldownPeriod", - }, - { - type: "event", - anonymous: false, - inputs: [ - { - name: "implementation", - internalType: "address", - type: "address", - indexed: true, - }, - ], - name: "Upgraded", - }, - { - type: "event", - anonymous: false, - inputs: [ - { - name: "account", - internalType: "address", - type: "address", - indexed: true, - }, - { - name: "amount", - internalType: "uint256", - type: "uint256", - indexed: false, - }, - ], - name: "Withdraw", - }, - { - type: "error", - inputs: [{ name: "target", internalType: "address", type: "address" }], - name: "AddressEmptyCode", - }, - { - type: "error", - inputs: [{ name: "account", internalType: "address", type: "address" }], - name: "AddressInsufficientBalance", - }, - { - type: "error", - inputs: [ - { name: "implementation", internalType: "address", type: "address" }, - ], - name: "ERC1967InvalidImplementation", - }, - { type: "error", inputs: [], name: "ERC1967NonPayable" }, - { type: "error", inputs: [], name: "EnforcedPause" }, - { type: "error", inputs: [], name: "ExpectedPause" }, - { type: "error", inputs: [], name: "FailedInnerCall" }, - { type: "error", inputs: [], name: "InvalidInitialization" }, - { type: "error", inputs: [], name: "NotInitializing" }, - { - type: "error", - inputs: [{ name: "owner", internalType: "address", type: "address" }], - name: "OwnableInvalidOwner", - }, - { - type: "error", - inputs: [{ name: "account", internalType: "address", type: "address" }], - name: "OwnableUnauthorizedAccount", - }, - { - type: "error", - inputs: [{ name: "token", internalType: "address", type: "address" }], - name: "SafeERC20FailedOperation", - }, - { type: "error", inputs: [], name: "UUPSUnauthorizedCallContext" }, - { - type: "error", - inputs: [{ name: "slot", internalType: "bytes32", type: "bytes32" }], - name: "UUPSUnsupportedProxiableUUID", - }, -] as const; - ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // MockFOXToken //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////