diff --git a/script/01_DeployUsdnWstethUsd.s.sol b/script/01_DeployUsdnWstethUsd.s.sol index eadcb9e4e..e64d35b65 100644 --- a/script/01_DeployUsdnWstethUsd.s.sol +++ b/script/01_DeployUsdnWstethUsd.s.sol @@ -141,9 +141,9 @@ contract DeployUsdnWstethUsd is UsdnWstethUsdConfig, Script { } /** - * @notice Initialize the USDN protocol with a ~2x leverage long position. + * @notice Initialize the USDN protocol with a ~2x leverage short position. * @param usdnProtocol The USDN protocol. - * @param wstEthOracleMiddleware The WstETH oracle middleware. + * @param wstEthOracleMiddleware The WUSDN oracle middleware. */ function _initializeProtocol(IUsdnProtocol usdnProtocol, WstEthOracleMiddlewareWithPyth wstEthOracleMiddleware) internal diff --git a/script/fork/DeployUsdnWstethFork.s.sol b/script/fork/deployFork_WstethUsdn/DeployUsdnWstethFork.s.sol similarity index 87% rename from script/fork/DeployUsdnWstethFork.s.sol rename to script/fork/deployFork_WstethUsdn/DeployUsdnWstethFork.s.sol index eff99d9f8..9a3f4dae4 100644 --- a/script/fork/DeployUsdnWstethFork.s.sol +++ b/script/fork/deployFork_WstethUsdn/DeployUsdnWstethFork.s.sol @@ -7,25 +7,25 @@ import { HugeUint } from "@smardex-solidity-libraries-1/HugeUint.sol"; import { UnsafeUpgrades } from "openzeppelin-foundry-upgrades/Upgrades.sol"; import { FixedPointMathLib } from "solady/src/utils/FixedPointMathLib.sol"; -import { MockChainlinkOnChain } from "../../test/unit/Middlewares/utils/MockChainlinkOnChain.sol"; -import { WstETH } from "../../test/utils/WstEth.sol"; -import { UsdnWstethUsdConfig } from "../deploymentConfigs/UsdnWstethUsdConfig.sol"; -import { Utils } from "../utils/Utils.s.sol"; +import { MockChainlinkOnChain } from "../../../test/unit/Middlewares/utils/MockChainlinkOnChain.sol"; +import { WstETH } from "../../../test/utils/WstEth.sol"; +import { UsdnWstethUsdConfig } from "../../deploymentConfigs/UsdnWstethUsdConfig.sol"; +import { Utils } from "../../utils/Utils.s.sol"; import { LiquidationRewardsManagerWstEth } from - "../../src/LiquidationRewardsManager/LiquidationRewardsManagerWstEth.sol"; + "../../../src/LiquidationRewardsManager/LiquidationRewardsManagerWstEth.sol"; import { MockWstEthOracleMiddlewareWithPyth } from - "../../src/OracleMiddleware/mock/MockWstEthOracleMiddlewareWithPyth.sol"; -import { Rebalancer } from "../../src/Rebalancer/Rebalancer.sol"; -import { Usdn } from "../../src/Usdn/Usdn.sol"; -import { Wusdn } from "../../src/Usdn/Wusdn.sol"; -import { UsdnProtocolFallback } from "../../src/UsdnProtocol/UsdnProtocolFallback.sol"; -import { UsdnProtocolImpl } from "../../src/UsdnProtocol/UsdnProtocolImpl.sol"; + "../../../src/OracleMiddleware/mock/MockWstEthOracleMiddlewareWithPyth.sol"; +import { Rebalancer } from "../../../src/Rebalancer/Rebalancer.sol"; +import { Usdn } from "../../../src/Usdn/Usdn.sol"; +import { Wusdn } from "../../../src/Usdn/Wusdn.sol"; +import { UsdnProtocolFallback } from "../../../src/UsdnProtocol/UsdnProtocolFallback.sol"; +import { UsdnProtocolImpl } from "../../../src/UsdnProtocol/UsdnProtocolImpl.sol"; import { UsdnProtocolConstantsLibrary as Constants } from - "../../src/UsdnProtocol/libraries/UsdnProtocolConstantsLibrary.sol"; -import { IWstETH } from "../../src/interfaces/IWstETH.sol"; -import { IUsdnProtocol } from "../../src/interfaces/UsdnProtocol/IUsdnProtocol.sol"; -import { IUsdnProtocolTypes as Types } from "../../src/interfaces/UsdnProtocol/IUsdnProtocolTypes.sol"; + "../../../src/UsdnProtocol/libraries/UsdnProtocolConstantsLibrary.sol"; +import { IWstETH } from "../../../src/interfaces/IWstETH.sol"; +import { IUsdnProtocol } from "../../../src/interfaces/UsdnProtocol/IUsdnProtocol.sol"; +import { IUsdnProtocolTypes as Types } from "../../../src/interfaces/UsdnProtocol/IUsdnProtocolTypes.sol"; contract DeployUsdnWstethFork is UsdnWstethUsdConfig, Script { address immutable CHAINLINK_ETH_PRICE_MOCKED = address(new MockChainlinkOnChain()); diff --git a/script/fork/deployFork.sh b/script/fork/deployFork_WstethUsdn/deployFork_WstethUsdn.sh similarity index 94% rename from script/fork/deployFork.sh rename to script/fork/deployFork_WstethUsdn/deployFork_WstethUsdn.sh index 86ba1b60a..d16a1a452 100755 --- a/script/fork/deployFork.sh +++ b/script/fork/deployFork_WstethUsdn/deployFork_WstethUsdn.sh @@ -2,14 +2,14 @@ # Path of the script folder (so that the script can be invoked from somewhere else than the project's root) SCRIPT_DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")") # Execute in the context of the project's root -pushd $SCRIPT_DIR/../.. >/dev/null +pushd $SCRIPT_DIR/../../.. >/dev/null # Anvil RPC URL rpcUrl=http://localhost:8545 # Anvil first test private key deployerPrivateKey=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 -forge script --non-interactive --private-key $deployerPrivateKey -f "$rpcUrl" ./script/fork/DeployUsdnWstethFork.s.sol:DeployUsdnWstethFork --broadcast +forge script --non-interactive --private-key $deployerPrivateKey -f "$rpcUrl" ./script/fork/deployFork_WstethUsdn/DeployUsdnWstethFork.s.sol:DeployUsdnWstethFork --broadcast chainId=$(cast chain-id -r "$rpcUrl") DEPLOYMENT_LOG=$(cat "./broadcast/DeployUsdnWstethFork.s.sol/$chainId/run-latest.json") diff --git a/script/fork/deployFork_WusdnEth/DeployUsdnWusdnFork.s.sol b/script/fork/deployFork_WusdnEth/DeployUsdnWusdnFork.s.sol new file mode 100644 index 000000000..c8497583c --- /dev/null +++ b/script/fork/deployFork_WusdnEth/DeployUsdnWusdnFork.s.sol @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.26; + +import { Script } from "forge-std/Script.sol"; + +import { HugeUint } from "@smardex-solidity-libraries-1/HugeUint.sol"; +import { Options, UnsafeUpgrades } from "openzeppelin-foundry-upgrades/Upgrades.sol"; +import { FixedPointMathLib } from "solady/src/utils/FixedPointMathLib.sol"; + +import { MockChainlinkOnChain } from "../../../test/unit/Middlewares/utils/MockChainlinkOnChain.sol"; +import { MockPyth } from "../../../test/unit/Middlewares/utils/MockPyth.sol"; +import { UsdnWusdnEthConfig } from "../../deploymentConfigs/UsdnWusdnEthConfig.sol"; +import { Utils } from "../../utils/Utils.s.sol"; + +import { LiquidationRewardsManagerWusdn } from + "../../../src/LiquidationRewardsManager/LiquidationRewardsManagerWusdn.sol"; +import { WusdnToEthOracleMiddlewareWithPyth } from + "../../../src/OracleMiddleware/WusdnToEthOracleMiddlewareWithPyth.sol"; +import { Rebalancer } from "../../../src/Rebalancer/Rebalancer.sol"; +import { UsdnNoRebase } from "../../../src/Usdn/UsdnNoRebase.sol"; +import { UsdnProtocolFallback } from "../../../src/UsdnProtocol/UsdnProtocolFallback.sol"; +import { UsdnProtocolImpl } from "../../../src/UsdnProtocol/UsdnProtocolImpl.sol"; +import { UsdnProtocolConstantsLibrary as Constants } from + "../../../src/UsdnProtocol/libraries/UsdnProtocolConstantsLibrary.sol"; +import { IWusdn } from "../../../src/interfaces/Usdn/IWusdn.sol"; +import { IUsdnProtocol } from "../../../src/interfaces/UsdnProtocol/IUsdnProtocol.sol"; +import { IUsdnProtocolTypes as Types } from "../../../src/interfaces/UsdnProtocol/IUsdnProtocolTypes.sol"; + +contract DeployUsdnWusdnFork is UsdnWusdnEthConfig, Script { + address immutable CHAINLINK_ETH_PRICE_MOCKED = address(new MockChainlinkOnChain()); + address immutable PYTH_ETH_PRICE_MOCKED = address(new MockPyth()); + uint256 constant ETH_PRICE = 3000 ether; // 3000 USDN per ETH + uint256 price = ((1 ether % ETH_PRICE) - 1 ether) / ETH_PRICE; // wUSDN price in ETH. We make the approximation that + // 1 USDN = 1 WUSDN + IWusdn immutable WUSDN; + Utils utils; + + constructor() UsdnWusdnEthConfig() { + UNDERLYING_ASSET = IWusdn(vm.envOr("UNDERLYING_ADDRESS", address(UNDERLYING_ASSET))); + WUSDN = IWusdn(address(UNDERLYING_ASSET)); + utils = new Utils(); + price = vm.envOr("START_PRICE", price); + } + + /** + * @notice Deploy the USDN ecosystem with WUSDN as the underlying token. + * @return wusdnToEthOracleMiddleware_ The oracle middleware to get the price of the WUSDN in ETH. + * @return liquidationRewardsManagerWusdn_ The liquidation rewards manager. + * @return rebalancer_ The rebalancer. + * @return usdnNoRebase_ The USDN token contract. + * @return usdnProtocol_ The USDN protocol contract. + */ + function run() + external + returns ( + WusdnToEthOracleMiddlewareWithPyth wusdnToEthOracleMiddleware_, + LiquidationRewardsManagerWusdn liquidationRewardsManagerWusdn_, + Rebalancer rebalancer_, + UsdnNoRebase usdnNoRebase_, + IUsdnProtocol usdnProtocol_, + address underlying_, // WUSDN + address sdex_ + ) + { + _setFeeCollector(msg.sender); + + (wusdnToEthOracleMiddleware_, liquidationRewardsManagerWusdn_, usdnNoRebase_) = + _deployAndSetPeripheralContracts(); + + usdnProtocol_ = _deployProtocol(initStorage); + _grantRequiredRoles(usdnProtocol_, usdnNoRebase_); + + rebalancer_ = _setRebalancer(usdnProtocol_); + + _initializeProtocol(usdnProtocol_); + _revokeRoles(usdnProtocol_); + + utils.validateProtocolConfig(usdnProtocol_, msg.sender); + + underlying_ = address(UNDERLYING_ASSET); + sdex_ = address(SDEX); + } + + /** + * @notice Deploy the oracle middleware, liquidation rewards manager and UsdnNoRebase contracts. Add them to the + * initialization struct. + * @dev As the USDN token doesn't rebase, there's no need to deploy the WUSDN contract, as wrapping is only useful + * to avoid messing with the token balances in smart contracts. + * @return wusdnToEthOracleMiddleware_ The oracle middleware that gets the price of the WUSDN in Eth. + * @return liquidationRewardsManagerWusdn_ The liquidation rewards manager. + * @return usdnNoRebase_ The USDN contract. + */ + function _deployAndSetPeripheralContracts() + internal + returns ( + WusdnToEthOracleMiddlewareWithPyth wusdnToEthOracleMiddleware_, + LiquidationRewardsManagerWusdn liquidationRewardsManagerWusdn_, + UsdnNoRebase usdnNoRebase_ + ) + { + vm.startBroadcast(); + liquidationRewardsManagerWusdn_ = new LiquidationRewardsManagerWusdn(WUSDN); + // MockPyth(PYTH_ETH_PRICE_MOCKED).setPrice(); // todo: link PYTH_ETH_PRICE_MOCKED to the input of this func + wusdnToEthOracleMiddleware_ = new WusdnToEthOracleMiddlewareWithPyth( + PYTH_ETH_PRICE_MOCKED, + PYTH_ETH_FEED_ID, + CHAINLINK_ETH_PRICE_MOCKED, + address(WUSDN.USDN()), + CHAINLINK_PRICE_VALIDITY + ); + // todo: disable verify signature on wusdnToEthOracleMiddleware_ + + usdnNoRebase_ = new UsdnNoRebase("Synthetic ETH", "syntETH"); + vm.stopBroadcast(); + + _setPeripheralContracts(wusdnToEthOracleMiddleware_, liquidationRewardsManagerWusdn_, usdnNoRebase_); + } + + /** + * @notice Deploy the USDN protocol. + * @param initStorage The initialization parameters struct. + * @return usdnProtocol_ The USDN protocol proxy. + */ + function _deployProtocol(Types.InitStorage storage initStorage) internal returns (IUsdnProtocol usdnProtocol_) { + // we need to allow external library linking and immutable variables in the openzeppelin module + Options memory opts; + opts.unsafeAllow = "external-library-linking,state-variable-immutable"; + + vm.startBroadcast(); + + UsdnProtocolFallback protocolFallback = new UsdnProtocolFallback(MAX_SDEX_BURN_RATIO, MAX_MIN_LONG_POSITION); + _setProtocolFallback(protocolFallback); + + address proxy = UnsafeUpgrades.deployUUPSProxy( + address(new UsdnProtocolImpl()), abi.encodeCall(UsdnProtocolImpl.initializeStorage, initStorage) + ); + + vm.stopBroadcast(); + + usdnProtocol_ = IUsdnProtocol(proxy); + } + + /** + * @notice Deploys and sets the rebalancer. + * @param usdnProtocol The USDN protocol. + * @return rebalancer_ The rebalancer. + */ + function _setRebalancer(IUsdnProtocol usdnProtocol) internal returns (Rebalancer rebalancer_) { + vm.startBroadcast(); + + rebalancer_ = new Rebalancer(usdnProtocol); + usdnProtocol.setRebalancer(rebalancer_); + + vm.stopBroadcast(); + } + + /** + * @notice Initializes the USDN protocol with a ~2x leverage short position. + * @param usdnProtocol The USDN protocol. + */ + function _initializeProtocol(IUsdnProtocol usdnProtocol) internal { + uint24 liquidationPenalty = usdnProtocol.getLiquidationPenalty(); + int24 tickSpacing = usdnProtocol.getTickSpacing(); + + // we want a leverage of ~2x so we get the current price from the middleware and divide it by two + uint128 desiredLiqPrice = uint128(price / 2); + // get the liquidation price with the tick rounding + uint128 liqPriceWithoutPenalty = usdnProtocol.getLiqPriceFromDesiredLiqPrice( + desiredLiqPrice, price, 0, HugeUint.wrap(0), tickSpacing, liquidationPenalty + ); + // get the total exposure of the wanted long position + uint256 positionTotalExpo = + FixedPointMathLib.fullMulDiv(INITIAL_LONG_AMOUNT, price, price - liqPriceWithoutPenalty); + // get the amount to deposit to reach a balanced state + uint256 depositAmount = positionTotalExpo - INITIAL_LONG_AMOUNT; + + vm.startBroadcast(); + WUSDN.approve(address(usdnProtocol), depositAmount + INITIAL_LONG_AMOUNT); + usdnProtocol.initialize(uint128(depositAmount), uint128(INITIAL_LONG_AMOUNT), desiredLiqPrice, ""); + vm.stopBroadcast(); + } + + /** + * @dev Grants the required roles for the deployment. + * @param usdnProtocol The deployed USDN protocol. + * @param usdnNoRebase The USDN token of the protocol. + */ + function _grantRequiredRoles(IUsdnProtocol usdnProtocol, UsdnNoRebase usdnNoRebase) internal { + vm.startBroadcast(); + + usdnProtocol.grantRole(Constants.ADMIN_SET_EXTERNAL_ROLE, msg.sender); + usdnProtocol.grantRole(Constants.SET_EXTERNAL_ROLE, msg.sender); + usdnProtocol.grantRole(Constants.ADMIN_SET_OPTIONS_ROLE, msg.sender); + usdnProtocol.grantRole(Constants.ADMIN_SET_PROTOCOL_PARAMS_ROLE, msg.sender); + usdnProtocol.grantRole(Constants.ADMIN_SET_USDN_PARAMS_ROLE, msg.sender); + usdnProtocol.grantRole(Constants.SET_OPTIONS_ROLE, msg.sender); + usdnProtocol.grantRole(Constants.SET_PROTOCOL_PARAMS_ROLE, msg.sender); + usdnProtocol.grantRole(Constants.SET_USDN_PARAMS_ROLE, msg.sender); + usdnProtocol.grantRole(Constants.ADMIN_CRITICAL_FUNCTIONS_ROLE, msg.sender); + usdnProtocol.grantRole(Constants.ADMIN_PROXY_UPGRADE_ROLE, msg.sender); + usdnProtocol.grantRole(Constants.ADMIN_PAUSER_ROLE, msg.sender); + usdnProtocol.grantRole(Constants.ADMIN_UNPAUSER_ROLE, msg.sender); + usdnProtocol.grantRole(Constants.CRITICAL_FUNCTIONS_ROLE, msg.sender); + usdnProtocol.grantRole(Constants.PROXY_UPGRADE_ROLE, msg.sender); + usdnProtocol.grantRole(Constants.PAUSER_ROLE, msg.sender); + usdnProtocol.grantRole(Constants.UNPAUSER_ROLE, msg.sender); + + usdnNoRebase.transferOwnership(address(usdnProtocol)); + + vm.stopBroadcast(); + } + + /** + * @dev Revokes the roles that were only necessary during the deployment. + * @param usdnProtocol The deployed USDN protocol. + */ + function _revokeRoles(IUsdnProtocol usdnProtocol) internal { + vm.startBroadcast(); + + usdnProtocol.revokeRole(Constants.SET_EXTERNAL_ROLE, msg.sender); + usdnProtocol.revokeRole(Constants.ADMIN_SET_EXTERNAL_ROLE, msg.sender); + + vm.stopBroadcast(); + } +} diff --git a/script/fork/deployFork_WusdnEth/deployFork_WusdnEth.sh b/script/fork/deployFork_WusdnEth/deployFork_WusdnEth.sh new file mode 100755 index 000000000..d6ea17c81 --- /dev/null +++ b/script/fork/deployFork_WusdnEth/deployFork_WusdnEth.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# Path of the script folder (so that the script can be invoked from somewhere else than the project's root) +SCRIPT_DIR=$(dirname -- "$(readlink -f -- "$BASH_SOURCE")") +# Execute in the context of the project's root +pushd $SCRIPT_DIR/../../.. >/dev/null + +# Anvil RPC URL +rpcUrl=http://localhost:8545 +# Anvil first test private key +deployerPrivateKey=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + +forge script --non-interactive --private-key $deployerPrivateKey -f "$rpcUrl" ./script/fork/deployFork_WusdnEth/DeployUsdnWusdnFork.s.sol:DeployUsdnWusdnFork --broadcast + +chainId=$(cast chain-id -r "$rpcUrl") +DEPLOYMENT_LOG=$(cat "./broadcast/DeployUsdnWusdnFork.s.sol/$chainId/run-latest.json") + +USDN_TX_HASH=$(echo "$DEPLOYMENT_LOG" | jq '.transactions[] | select(.contractName == "Usdn" and .transactionType == "CREATE") | .hash') +USDN_RECEIPT=$(echo "$DEPLOYMENT_LOG" | jq ".receipts[] | select(.transactionHash == $USDN_TX_HASH)") +USDN_PROTOCOL_TX_HASH=$(echo "$DEPLOYMENT_LOG" | jq '.transactions[] | select(.contractName == "ERC1967Proxy" and .transactionType == "CREATE") | .hash') +USDN_PROTOCOL_RECEIPT=$(echo "$DEPLOYMENT_LOG" | jq ".receipts[] | select(.transactionHash == $USDN_PROTOCOL_TX_HASH)") + +USDN_PROTOCOL_ADDRESS=$(echo "$DEPLOYMENT_LOG" | jq '.returns.usdnProtocol_.value' | xargs printf "%s\n") + +FORK_ENV_DUMP=$( + cat < .env.fork + +popd >/dev/null