diff --git a/.gitignore b/.gitignore index 13263803..1243d666 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ docs/ .claude/ .treb/config.local.json node_modules +.treb/priv/ diff --git a/.gitmodules b/.gitmodules index 50e71937..0e2586c9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "lib/mento-router"] path = lib/mento-router url = https://github.com/mento-protocol/mento-router +[submodule "lib/bold"] + path = lib/bold + url = https://github.com/mento-protocol/bold diff --git a/foundry.lock b/foundry.lock index b9583fff..e1995b39 100644 --- a/foundry.lock +++ b/foundry.lock @@ -1,4 +1,7 @@ { + "lib/bold": { + "rev": "073a25f049f423b3336e3785d5f1e13235ced07b" + }, "lib/forge-std": { "rev": "8bbcf6e3f8f62f419e5429a0bd89331c85c37824" }, diff --git a/foundry.toml b/foundry.toml index 3238fd46..4f864a9b 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,12 +3,20 @@ src = "src" out = "out" libs = ["lib"] optimizer = true +ffi = true +additional_compiler_profiles = [ + { name = "no-opt", optimizer_runs = 0 }, +] +compilation_restrictions = [ + { paths = "lib/bold/contracts/src/TroveManager.sol", optimizer_runs = 0 }, +] fs_permissions = [ { access = "read-write", path = "cache"}, + { access = "read-write", path = "out/"}, { access = "read", path = ".treb/registry.json"}, { access = "read", path = ".treb/addressbook.json"}, - { access = "read", path = "script/networks" }, - { access = "read", path = "./mgps/"} + { access = "read", path = "script/config/"}, + { access = "read", path = "mgps/"} ] [lint] diff --git a/lib/bold b/lib/bold new file mode 160000 index 00000000..858ef07a --- /dev/null +++ b/lib/bold @@ -0,0 +1 @@ +Subproject commit 858ef07a497d748cdf65c602a56b4de901d4080e diff --git a/remappings.txt b/remappings.txt index 742f4c5d..e878e7c8 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,5 +1,6 @@ src/=src/ lib/mento-core/lib/bold/:openzeppelin-contracts/=lib/mento-core/lib/bold/contracts/lib/openzeppelin-contracts/ +lib/bold/:openzeppelin-contracts/=lib/bold/contracts/lib/openzeppelin-contracts/ openzeppelin-contracts/=lib/mento-core/lib/openzeppelin-contracts-next/ safe-smart-account/=lib/treb-sol/lib/safe-utils/lib/safe-smart-account/contracts/ mento-core/=lib/mento-core/contracts/ diff --git a/script/config/ILiquityConfig.sol b/script/config/ILiquityConfig.sol new file mode 100644 index 00000000..43735c17 --- /dev/null +++ b/script/config/ILiquityConfig.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface ILiquityConfig { + struct LiquityInstanceConfig { + // ── Instance identity ──────────────────────────────────────────── + /// @dev Label used for proxy deployments, e.g. "GBPm" + string proxyLabel; + /// @dev Label used for singleton CREATE3 deployments, e.g. "v3.0.0-GBPm" + string singletonLabel; + // ── Registry lookup keys ───────────────────────────────────────── + string debtTokenLabel; // e.g. "StableTokenV3:GBPm" + string collateralTokenLabel; // e.g. "StableTokenV3:USDm" + string liquidityStrategyLabel; // e.g. "CDPLiquidityStrategy" + string gasTokenLabel; // e.g. "StableTokenV3:USDm" + string oracleAdapterLabel; // e.g. "OracleAdapter" + // ── Addresses ──────────────────────────────────────────────────── + address rateFeedID; + address watchdog; + address owner; + address yieldSplitAddress; + // ── FXPriceFeed ────────────────────────────────────────────────── + bool invertRateFeed; + uint256 l2SequencerGracePeriod; + // ── Collateral / TroveManager params ───────────────────────────── + uint256 CCR; + uint256 MCR; + uint256 BCR; + uint256 SCR; + uint256 liquidationPenaltySP; + uint256 liquidationPenaltyRedistribution; + // ── SystemParams: debt ─────────────────────────────────────────── + uint256 minDebt; + // ── SystemParams: gas compensation ─────────────────────────────── + uint256 collGasCompensationDivisor; + uint256 collGasCompensationCap; + uint256 ethGasCompensation; + // ── SystemParams: interest ─────────────────────────────────────── + uint256 minAnnualInterestRate; + // ── SystemParams: redemption ───────────────────────────────────── + uint256 redemptionFeeFloor; + uint256 initialBaseRate; + uint256 redemptionMinuteDecayFactor; + uint256 redemptionBeta; + // ── SystemParams: stability pool ───────────────────────────────── + uint256 spYieldSplit; + uint256 minBoldInSP; + uint256 minBoldAfterRebalance; + // ── NFT Metadata assets ───────────────────────────────────────── + /// @dev Base directory for asset files, relative to project root + /// e.g. "lib/bold/contracts/utils/assets/" + string metadataAssetsBasePath; + /// @dev Filename for the debt token logo (stored under "BOLD" key) + string debtTokenLogoFile; + /// @dev Filename for the collateral token logo + string collateralTokenLogoFile; + /// @dev Must match IERC20Metadata(collateralToken).symbol() + string collateralTokenSymbol; + /// @dev Filename for the font file (stored under "geist" key) + string fontFile; + } + + function get() external view returns (LiquityInstanceConfig memory); +} diff --git a/script/config/LiquityConfig.sol b/script/config/LiquityConfig.sol new file mode 100644 index 00000000..5fe7443a --- /dev/null +++ b/script/config/LiquityConfig.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Vm} from "forge-std/Vm.sol"; +import {ILiquityConfig} from "./ILiquityConfig.sol"; + +// ── Concrete configs ───────────────────────────────────────────────────────── +// Imported here so Foundry compiles their artifacts, enabling vm.deployCode() +// to find them by contract name at script run time. +import "./liquity/LiquityConfig_celo_sepolia_GBPm.sol"; +import "./liquity/LiquityConfig_celo_GBPm.sol"; +import "./liquity/LiquityConfig_anvil_GBPm.sol"; + +/** + * @notice Loader library used by deployment scripts. + * @dev Resolves the concrete config contract from the NETWORK env var and the + * token parameter, e.g. LiquityConfigLib.get("GBPm") on network "anvil" + * will deploy LiquityConfig_anvil_GBPm. + * vm.deployCode() instantiates the config locally in Foundry's simulation + * VM without broadcasting it as an on-chain transaction. + */ +library LiquityConfigLib { + address private constant VM_ADDRESS = + address(uint160(uint256(keccak256("hevm cheat code")))); + Vm private constant vm = Vm(VM_ADDRESS); + + function get( + string memory token + ) internal returns (ILiquityConfig.LiquityInstanceConfig memory) { + string memory network = vm.envString("NETWORK"); + string memory name = string.concat( + "LiquityConfig_", + network, + "_", + token + ); + address config = vm.deployCode(name); + require( + config != address(0), + string.concat("LiquityConfig: failed to deploy ", name) + ); + return ILiquityConfig(config).get(); + } +} diff --git a/script/config/liquity/LiquityConfig_anvil_GBPm.sol b/script/config/liquity/LiquityConfig_anvil_GBPm.sol new file mode 100644 index 00000000..b1eef4a3 --- /dev/null +++ b/script/config/liquity/LiquityConfig_anvil_GBPm.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {ILiquityConfig} from "../ILiquityConfig.sol"; + +/** + * @notice Liquity GBPm/USDm instance config for Anvil (local Monad mainnet fork). + * @dev gasTokenLabel == collateralTokenLabel: USDm serves as both collateral and gas token. + */ +contract LiquityConfig_anvil_GBPm is ILiquityConfig { + function get() + external + pure + override + returns (ILiquityConfig.LiquityInstanceConfig memory) + { + return + ILiquityConfig.LiquityInstanceConfig({ + proxyLabel: "GBPm", + singletonLabel: "v3.0.0-GBPm", + // ── Registry lookup keys ──────────────────────────────── + debtTokenLabel: "StableTokenV3:GBPm", + collateralTokenLabel: "StableTokenV3:USDm", + liquidityStrategyLabel: "CDPLiquidityStrategy", + gasTokenLabel: "StableTokenV3:USDm", + oracleAdapterLabel: "OracleAdapter", + // ── Addresses ────────────────────────────────────────── + rateFeedID: 0x00000000000000000000000000000000075BCd15, + watchdog: 0x00000000000000000000000000000002DfDC1c3E, + owner: 0x000000000000000000000000000000000001E240, + yieldSplitAddress: 0x000000000000000000000000000000000001e241, + // ── FXPriceFeed ──────────────────────────────────────── + invertRateFeed: false, + l2SequencerGracePeriod: 1200, + // ── Collateral params ────────────────────────────────── + CCR: 1500000000000000000, + MCR: 1100000000000000000, + BCR: 100000000000000000, + SCR: 1100000000000000000, + liquidationPenaltySP: 50000000000000000, + liquidationPenaltyRedistribution: 100000000000000000, + // ── SystemParams: debt ───────────────────────────────── + minDebt: 100000000000000000000, + // ── SystemParams: gas compensation ───────────────────── + collGasCompensationDivisor: 200, + collGasCompensationCap: 2000000000000000000, + ethGasCompensation: 37500000000000000, + // ── SystemParams: interest ───────────────────────────── + minAnnualInterestRate: 5000000000000000, + // ── SystemParams: redemption ─────────────────────────── + redemptionFeeFloor: 2500000000000000, + initialBaseRate: 1000000000000000000, + redemptionMinuteDecayFactor: 998076443575628800, + redemptionBeta: 1, + // ── SystemParams: stability pool ─────────────────────── + spYieldSplit: 750000000000000000, + minBoldInSP: 1000000000000000000, + minBoldAfterRebalance: 1000000000000000000000, + // ── NFT Metadata assets ─────────────────────────────── + metadataAssetsBasePath: "script/config/liquity/assets/", + debtTokenLogoFile: "GBPm.svg", + collateralTokenLogoFile: "USDm.svg", + collateralTokenSymbol: "USDm", + fontFile: "geist.txt" + }); + } +} diff --git a/script/config/liquity/LiquityConfig_celo_GBPm.sol b/script/config/liquity/LiquityConfig_celo_GBPm.sol new file mode 100644 index 00000000..ec8098ff --- /dev/null +++ b/script/config/liquity/LiquityConfig_celo_GBPm.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {ILiquityConfig} from "../ILiquityConfig.sol"; + +/** + * @notice Liquity GBPm/USDm instance config for Celo. + */ +contract LiquityConfig_celo_GBPm is ILiquityConfig { + function get() + external + pure + override + returns (ILiquityConfig.LiquityInstanceConfig memory) + { + return + ILiquityConfig.LiquityInstanceConfig({ + proxyLabel: "GBPm", + singletonLabel: "v3.0.0-GBPm", + // ── Registry lookup keys ──────────────────────────────── + // TODO: change to labels used in prestage Celo script + debtTokenLabel: "StableTokenV3:GBPm", + collateralTokenLabel: "StableTokenV3:USDm", + liquidityStrategyLabel: "CDPLiquidityStrategy", + gasTokenLabel: "StableTokenV3:USDm", + oracleAdapterLabel: "OracleAdapter", + // ── Addresses ────────────────────────────────────────── + rateFeedID: 0xf590b62f9cfcc6409075b1ecAc8176fe25744B88, // GBP/USD + watchdog: 0x287810F677516f10993ff63a520aAD5509F35796, // TODO: change to FXPriceFeed Watchdog Celo + owner: 0x287810F677516f10993ff63a520aAD5509F35796, // TODO: change to Owner Celo + yieldSplitAddress: 0x287810F677516f10993ff63a520aAD5509F35796, // TODO: change to Yield Split Address Celo + // ── FXPriceFeed ──────────────────────────────────────── + invertRateFeed: true, // SortedOracles: GBP/USD is inverted as (USD/GBP) + l2SequencerGracePeriod: 1200, // 20 minutes + // ── Collateral params ────────────────────────────────── + CCR: 1e18 * 1.35, // 135% + MCR: 1e18 * 1.1, // 110% + BCR: 1e18 * 0.1, // 10% + SCR: 1e18 * 1.1, // 110% + liquidationPenaltySP: 1e18 * 0.05, // 5% + liquidationPenaltyRedistribution: 1e18 * 0.1, // 10% + // ── SystemParams: debt ───────────────────────────────── + minDebt: 1_000e18, // 1,000 GBPm + // ── SystemParams: gas compensation ───────────────────── + collGasCompensationDivisor: 200, + collGasCompensationCap: 10e18, // 10 USDm + ethGasCompensation: 1e18, // 1 CELO + // ── SystemParams: interest ───────────────────────────── + minAnnualInterestRate: 1e18 * 0.002, // 0.2% + // ── SystemParams: redemption ─────────────────────────── + redemptionFeeFloor: 1e18 * 0.005, // 0.5% + initialBaseRate: 1e18, // 100% + redemptionMinuteDecayFactor: 1e18 * 0.9885140204, // 60 minutes half-life time + redemptionBeta: 1, + // ── SystemParams: stability pool ─────────────────────── + spYieldSplit: 1e18 * 0.75, // 75% + minBoldInSP: 1e18, // 1 GBPm + minBoldAfterRebalance: 5_000e18, // 5_000 GBPm + // ── NFT Metadata assets ─────────────────────────────── + metadataAssetsBasePath: "script/config/liquity/assets/", + debtTokenLogoFile: "GBPm.svg", + collateralTokenLogoFile: "USDm.svg", + collateralTokenSymbol: "USDm", + fontFile: "geist.txt" + }); + } +} \ No newline at end of file diff --git a/script/config/liquity/LiquityConfig_celo_sepolia_GBPm.sol b/script/config/liquity/LiquityConfig_celo_sepolia_GBPm.sol new file mode 100644 index 00000000..ce5c9ee9 --- /dev/null +++ b/script/config/liquity/LiquityConfig_celo_sepolia_GBPm.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {ILiquityConfig} from "../ILiquityConfig.sol"; + +/** + * @notice Liquity GBPm/USDm instance config for Celo. + */ +contract LiquityConfig_celo_sepolia_GBPm is ILiquityConfig { + function get() + external + pure + override + returns (ILiquityConfig.LiquityInstanceConfig memory) + { + return + ILiquityConfig.LiquityInstanceConfig({ + proxyLabel: "GBPm", + singletonLabel: "v3.0.0-GBPm", + // ── Registry lookup keys ──────────────────────────────── + // TODO: change to labels used in prestage Celo script + debtTokenLabel: "cGBP", + collateralTokenLabel: "cUSD", + liquidityStrategyLabel: "CDPLiquidityStrategy", + gasTokenLabel: "cUSD", + oracleAdapterLabel: "OracleAdapter", + // ── Addresses ────────────────────────────────────────── + rateFeedID: 0xf590b62f9cfcc6409075b1ecAc8176fe25744B88, // GBP/USD + watchdog: 0x287810F677516f10993ff63a520aAD5509F35796, // TODO: change to FXPriceFeed Watchdog Celo + owner: 0x287810F677516f10993ff63a520aAD5509F35796, // TODO: change to Owner Celo + yieldSplitAddress: 0x287810F677516f10993ff63a520aAD5509F35796, // TODO: change to Yield Split Address Celo + // ── FXPriceFeed ──────────────────────────────────────── + invertRateFeed: true, // SortedOracles: GBP/USD is inverted as (USD/GBP) + l2SequencerGracePeriod: 1200, // 20 minutes + // ── Collateral params ────────────────────────────────── + CCR: 1e18 * 1.35, // 135% + MCR: 1e18 * 1.1, // 110% + BCR: 1e18 * 0.1, // 10% + SCR: 1e18 * 1.1, // 110% + liquidationPenaltySP: 1e18 * 0.05, // 5% + liquidationPenaltyRedistribution: 1e18 * 0.1, // 10% + // ── SystemParams: debt ───────────────────────────────── + minDebt: 1_000e18, // 1,000 GBPm + // ── SystemParams: gas compensation ───────────────────── + collGasCompensationDivisor: 200, + collGasCompensationCap: 10e18, // 10 USDm + ethGasCompensation: 1e18, // 1 CELO + // ── SystemParams: interest ───────────────────────────── + minAnnualInterestRate: 1e18 * 0.002, // 0.2% + // ── SystemParams: redemption ─────────────────────────── + redemptionFeeFloor: 1e18 * 0.005, // 0.5% + initialBaseRate: 1e18, // 100% + redemptionMinuteDecayFactor: 1e18 * 0.9885140204, // 60 minutes half-life time + redemptionBeta: 1, + // ── SystemParams: stability pool ─────────────────────── + spYieldSplit: 1e18 * 0.75, // 75% + minBoldInSP: 1e18, // 1 GBPm + minBoldAfterRebalance: 5_000e18, // 5_000 GBPm + // ── NFT Metadata assets ─────────────────────────────── + metadataAssetsBasePath: "script/config/liquity/assets/", + debtTokenLogoFile: "GBPm.svg", + collateralTokenLogoFile: "USDm.svg", + collateralTokenSymbol: "USDm", + fontFile: "geist.txt" + }); + } +} diff --git a/script/config/liquity/assets/AUDm.svg b/script/config/liquity/assets/AUDm.svg new file mode 100644 index 00000000..56ac2f31 --- /dev/null +++ b/script/config/liquity/assets/AUDm.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/script/config/liquity/assets/BRLm.svg b/script/config/liquity/assets/BRLm.svg new file mode 100644 index 00000000..eaec344a --- /dev/null +++ b/script/config/liquity/assets/BRLm.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/script/config/liquity/assets/CADm.svg b/script/config/liquity/assets/CADm.svg new file mode 100644 index 00000000..3661dd15 --- /dev/null +++ b/script/config/liquity/assets/CADm.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/script/config/liquity/assets/CHFm.svg b/script/config/liquity/assets/CHFm.svg new file mode 100644 index 00000000..292e13c9 --- /dev/null +++ b/script/config/liquity/assets/CHFm.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/script/config/liquity/assets/COPm.svg b/script/config/liquity/assets/COPm.svg new file mode 100644 index 00000000..cadf3e4a --- /dev/null +++ b/script/config/liquity/assets/COPm.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/script/config/liquity/assets/EURm.svg b/script/config/liquity/assets/EURm.svg new file mode 100644 index 00000000..047439bc --- /dev/null +++ b/script/config/liquity/assets/EURm.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/script/config/liquity/assets/GBPm.svg b/script/config/liquity/assets/GBPm.svg new file mode 100644 index 00000000..d52e537f --- /dev/null +++ b/script/config/liquity/assets/GBPm.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/script/config/liquity/assets/GHSm.svg b/script/config/liquity/assets/GHSm.svg new file mode 100644 index 00000000..79b9020a --- /dev/null +++ b/script/config/liquity/assets/GHSm.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/script/config/liquity/assets/JPYm.svg b/script/config/liquity/assets/JPYm.svg new file mode 100644 index 00000000..2aa458df --- /dev/null +++ b/script/config/liquity/assets/JPYm.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/script/config/liquity/assets/KESm.svg b/script/config/liquity/assets/KESm.svg new file mode 100644 index 00000000..9958015f --- /dev/null +++ b/script/config/liquity/assets/KESm.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/script/config/liquity/assets/NGNm.svg b/script/config/liquity/assets/NGNm.svg new file mode 100644 index 00000000..96439d28 --- /dev/null +++ b/script/config/liquity/assets/NGNm.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/script/config/liquity/assets/PHPm.svg b/script/config/liquity/assets/PHPm.svg new file mode 100644 index 00000000..d374124e --- /dev/null +++ b/script/config/liquity/assets/PHPm.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/script/config/liquity/assets/USDm.svg b/script/config/liquity/assets/USDm.svg new file mode 100644 index 00000000..f9e5f3b9 --- /dev/null +++ b/script/config/liquity/assets/USDm.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/script/config/liquity/assets/XOFm.svg b/script/config/liquity/assets/XOFm.svg new file mode 100644 index 00000000..c6a59239 --- /dev/null +++ b/script/config/liquity/assets/XOFm.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/script/config/liquity/assets/ZARm.svg b/script/config/liquity/assets/ZARm.svg new file mode 100644 index 00000000..7b34cd9b --- /dev/null +++ b/script/config/liquity/assets/ZARm.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/script/config/liquity/assets/geist.txt b/script/config/liquity/assets/geist.txt new file mode 100644 index 00000000..a14ec963 --- /dev/null +++ b/script/config/liquity/assets/geist.txt @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/script/deploy/v3/DeployCDPLiquidityStrategy.s.sol b/script/deploy/v3/DeployCDPLiquidityStrategy.s.sol index 37c3c50d..e4ea3758 100644 --- a/script/deploy/v3/DeployCDPLiquidityStrategy.s.sol +++ b/script/deploy/v3/DeployCDPLiquidityStrategy.s.sol @@ -5,7 +5,6 @@ import {TrebScript} from "lib/treb-sol/src/TrebScript.sol"; import {Senders} from "lib/treb-sol/src/internal/sender/Senders.sol"; import {Deployer} from "treb-sol/src/internal/sender/Deployer.sol"; import {ProxyHelper} from "script/helpers/ProxyHelper.sol"; -import {AddressbookHelper} from "script/helpers/AddressbookHelper.sol"; import {PostChecksHelper} from "script/helpers/PostChecksHelper.sol"; import {Config, IMentoConfig} from "script/config/Config.sol"; @@ -14,7 +13,6 @@ import {ICDPLiquidityStrategy} from "mento-core/interfaces/ICDPLiquidityStrategy contract DeployCDPLiquidityStrategy is TrebScript, - AddressbookHelper, ProxyHelper, PostChecksHelper { @@ -23,21 +21,22 @@ contract DeployCDPLiquidityStrategy is using Senders for Senders.Sender; using GnosisSafe for GnosisSafe.Sender; - address multisig; address cdpLiquidityStrategyImpl; address cdpLiquidityStrategy; IMentoConfig config; + Senders.Sender deployer; + Senders.Sender owner; string constant label = "v3.0.0"; function setUp() public { - multisig = lookupAddressbook("MigrationMultisig"); config = Config.get(); } - /// @custom:senders deployer + /// @custom:senders deployer, migrationOwner function run() public broadcast { - Senders.Sender storage deployer = sender("deployer"); + deployer = sender("deployer"); + owner = sender("migrationOwner"); cdpLiquidityStrategyImpl = deployer .create3("CDPLiquidityStrategy") @@ -52,7 +51,7 @@ contract DeployCDPLiquidityStrategy is cdpLiquidityStrategyImpl, abi.encodeWithSelector( ICDPLiquidityStrategy.initialize.selector, - multisig + owner.account ) ); postChecks(); @@ -64,6 +63,6 @@ contract DeployCDPLiquidityStrategy is cdpLiquidityStrategy, cdpLiquidityStrategyImpl ); - verifyOwnership("CDPLiquidityStrategy", cdpLiquidityStrategy, multisig); + verifyOwnership("CDPLiquidityStrategy", cdpLiquidityStrategy, owner.account); } } diff --git a/script/deploy/v3/DeployLiquity.s.sol b/script/deploy/v3/DeployLiquity.s.sol new file mode 100644 index 00000000..522e9ed2 --- /dev/null +++ b/script/deploy/v3/DeployLiquity.s.sol @@ -0,0 +1,811 @@ +// solhint-disable max-line-length, function-max-lines +// SPDX-License-Identifier: MIT +pragma solidity ^0.8; + +import {IBoldToken, IERC20Metadata} from "bold/src/Interfaces/IBoldToken.sol"; +import {StabilityPool} from "bold/src/StabilityPool.sol"; +import {Ownable} from "openzeppelin-contracts/contracts/access/Ownable.sol"; +import {SystemParams} from "bold/src/SystemParams.sol"; +import {FXPriceFeed} from "bold/src/PriceFeeds/FXPriceFeed.sol"; +import {AddressesRegistry} from "bold/src/AddressesRegistry.sol"; +import {ActivePool} from "bold/src/ActivePool.sol"; +import {BorrowerOperations} from "bold/src/BorrowerOperations.sol"; +import {TroveManager} from "bold/src/TroveManager.sol"; +import {TroveNFT} from "bold/src/TroveNFT.sol"; +import {CollSurplusPool} from "bold/src/CollSurplusPool.sol"; +import {DefaultPool} from "bold/src/DefaultPool.sol"; +import {GasPool} from "bold/src/GasPool.sol"; +import {HintHelpers} from "bold/src/HintHelpers.sol"; +import {MultiTroveGetter} from "bold/src/MultiTroveGetter.sol"; +import {SortedTroves} from "bold/src/SortedTroves.sol"; +import {CollateralRegistry} from "bold/src/CollateralRegistry.sol"; +import {ICollSurplusPool} from "bold/src/Interfaces/ICollSurplusPool.sol"; +import {IDefaultPool} from "bold/src/Interfaces/IDefaultPool.sol"; +import {IHintHelpers} from "bold/src/Interfaces/IHintHelpers.sol"; +import {IMultiTroveGetter} from "bold/src/Interfaces/IMultiTroveGetter.sol"; +import {ISortedTroves} from "bold/src/Interfaces/ISortedTroves.sol"; +import {IStabilityPool} from "bold/src/Interfaces/IStabilityPool.sol"; +import {ITroveManager} from "bold/src/Interfaces/ITroveManager.sol"; +import {IBorrowerOperations} from "bold/src/Interfaces/IBorrowerOperations.sol"; +import {IAddressesRegistry} from "bold/src/Interfaces/IAddressesRegistry.sol"; +import {IActivePool} from "bold/src/Interfaces/IActivePool.sol"; +import {ITroveNFT} from "bold/src/Interfaces/ITroveNFT.sol"; +import {ISystemParams} from "bold/src/Interfaces/ISystemParams.sol"; +import {ICollateralRegistry} from "bold/src/Interfaces/ICollateralRegistry.sol"; +import {IMetadataNFT, MetadataNFT} from "bold/src/NFTMetadata/MetadataNFT.sol"; +import {FixedAssetReader} from "bold/src/NFTMetadata/utils/FixedAssets.sol"; +import {IPriceFeed} from "bold/src/Interfaces/IPriceFeed.sol"; +import {IInterestRouter} from "bold/src/Interfaces/IInterestRouter.sol"; + +import "forge-std/console2.sol"; +import {Base64} from "Solady/utils/Base64.sol"; +import {TrebScript} from "treb-sol/src/TrebScript.sol"; +import {Senders} from "treb-sol/src/internal/sender/Senders.sol"; +import {Deployer} from "treb-sol/src/internal/sender/Deployer.sol"; +import {GnosisSafe} from "treb-sol/src/internal/sender/GnosisSafeSender.sol"; +import {ProxyHelper} from "script/helpers/ProxyHelper.sol"; +import {SSTORE2DataPointer} from "script/helpers/SSTORE2DataPointer.sol"; +import {ILiquityConfig} from "script/config/ILiquityConfig.sol"; +import {LiquityConfigLib} from "script/config/LiquityConfig.sol"; +contract DeployLiquityV2 is TrebScript, ProxyHelper { + using Deployer for Senders.Sender; + using Deployer for Deployer.Deployment; + using Senders for Senders.Sender; + using GnosisSafe for GnosisSafe.Sender; + + struct LiquityContractAddresses { + address addressesRegistry; + address activePool; + address borrowerOperations; + address collSurplusPool; + address collateralRegistry; + address defaultPool; + address hintHelpers; + address sortedTroves; + address stabilityPoolProxy; + address systemParamsProxy; + address troveManager; + address troveNFT; + address metadataNFT; + address multiTroveGetter; + address priceFeedProxy; + address gasPool; + address interestRouter; + } + + struct LiquityContractImplementationsAddresses { + address fxPriceFeedImplementation; + address stabilityPoolImplementation; + address systemParamsImplementation; + } + + address debtToken; // resolved from cfg.debtTokenLabel + address collateralToken; // resolved from cfg.collateralTokenLabel + address gasToken; // resolved from cfg.gasTokenLabel + + address oracleAdapter; // OracleAdapter Proxy needs to be deployed first + address cdpLiquidityStrategy; // resolved from cfg.liquidityStrategyLabel + + ILiquityConfig.LiquityInstanceConfig cfg; + + LiquityContractAddresses deployedContracts; + LiquityContractAddresses precomputedAddresses; + LiquityContractImplementationsAddresses upgradeableContractsImplementations; + Senders.Sender deployer; + + // UpgradeableContracts: + // - FXPriceFeed + // - StabilityPool + // - SystemParams + + /// @custom:env {string} token + /// @custom:senders deployer + function run() public broadcast { + deployer = sender("deployer"); + cfg = LiquityConfigLib.get(vm.envString("token")); + + oracleAdapter = lookupProxyOrFail(cfg.oracleAdapterLabel); + debtToken = lookupProxyOrFail(cfg.debtTokenLabel); + collateralToken = lookupProxyOrFail(cfg.collateralTokenLabel); + cdpLiquidityStrategy = lookupProxyOrFail(cfg.liquidityStrategyLabel); + gasToken = lookupProxyOrFail(cfg.gasTokenLabel); + + deployAndConnectContracts(); + } + + function deployAndConnectContracts() public { + _deploySystemParams(); + + deployedContracts.addressesRegistry = deployer + .create3("AddressesRegistry.sol:AddressesRegistry") + .setLabel(cfg.singletonLabel) + .deploy(abi.encode(deployer.account)); + + // Pre-compute all addresses before any contract that depends on them + precomputedAddresses.troveManager = _predict( + "TroveManager.sol:TroveManager", + deployer, + cfg.singletonLabel + ); + + _deployCollateralRegistry(); + + deployedContracts.hintHelpers = deployer + .create3("HintHelpers.sol:HintHelpers") + .setLabel(cfg.singletonLabel) + .deploy( + abi.encode( + ICollateralRegistry(deployedContracts.collateralRegistry), + ISystemParams(deployedContracts.systemParamsProxy) + ) + ); + + deployedContracts.multiTroveGetter = deployer + .create3("MultiTroveGetter.sol:MultiTroveGetter") + .setLabel(cfg.singletonLabel) + .deploy( + abi.encode( + ICollateralRegistry(deployedContracts.collateralRegistry) + ) + ); + + precomputedAddresses.borrowerOperations = _predict( + "BorrowerOperations.sol:BorrowerOperations", + deployer, + cfg.singletonLabel + ); + precomputedAddresses.troveNFT = _predict( + "TroveNFT.sol:TroveNFT", + deployer, + cfg.singletonLabel + ); + precomputedAddresses.stabilityPoolProxy = predictProxy( + deployer, + string.concat("StabilityPool:", cfg.proxyLabel) + ); + precomputedAddresses.activePool = _predict( + "ActivePool.sol:ActivePool", + deployer, + cfg.singletonLabel + ); + precomputedAddresses.defaultPool = _predict( + "DefaultPool.sol:DefaultPool", + deployer, + cfg.singletonLabel + ); + precomputedAddresses.gasPool = _predict( + "GasPool.sol:GasPool", + deployer, + cfg.singletonLabel + ); + precomputedAddresses.collSurplusPool = _predict( + "CollSurplusPool.sol:CollSurplusPool", + deployer, + cfg.singletonLabel + ); + precomputedAddresses.sortedTroves = _predict( + "SortedTroves.sol:SortedTroves", + deployer, + cfg.singletonLabel + ); + + _deployFXPriceFeed(precomputedAddresses.borrowerOperations); + _deployMetadata(); + + IAddressesRegistry( + deployer.harness(deployedContracts.addressesRegistry) + ).setAddresses(_buildAddressVars()); + + deployedContracts.borrowerOperations = deployer + .create3("BorrowerOperations.sol:BorrowerOperations") + .setLabel(cfg.singletonLabel) + .deploy( + abi.encode( + IAddressesRegistry(deployedContracts.addressesRegistry), + ISystemParams(deployedContracts.systemParamsProxy) + ) + ); + + deployedContracts.troveManager = deployer + .create3("TroveManager.sol:TroveManager") + .setLabel(cfg.singletonLabel) + .deploy( + abi.encode( + IAddressesRegistry(deployedContracts.addressesRegistry), + ISystemParams(deployedContracts.systemParamsProxy) + ) + ); + + deployedContracts.troveNFT = deployer + .create3("TroveNFT.sol:TroveNFT") + .setLabel(cfg.singletonLabel) + .deploy( + abi.encode( + IAddressesRegistry(deployedContracts.addressesRegistry) + ) + ); + + upgradeableContractsImplementations + .stabilityPoolImplementation = deployer + .create3("StabilityPool.sol:StabilityPool") + .setLabel(cfg.singletonLabel) + .deploy( + abi.encode( + true, + ISystemParams(deployedContracts.systemParamsProxy) + ) + ); + + deployedContracts.stabilityPoolProxy = deployOztupProxy( + deployer, + string.concat("StabilityPool:", cfg.proxyLabel), + upgradeableContractsImplementations.stabilityPoolImplementation, + abi.encodeWithSelector( + StabilityPool.initialize.selector, + IAddressesRegistry(deployedContracts.addressesRegistry) + ) + ); + + deployedContracts.activePool = deployer + .create3("ActivePool.sol:ActivePool") + .setLabel(cfg.singletonLabel) + .deploy( + abi.encode( + deployedContracts.addressesRegistry, + ISystemParams(deployedContracts.systemParamsProxy) + ) + ); + + deployedContracts.defaultPool = deployer + .create3("DefaultPool.sol:DefaultPool") + .setLabel(cfg.singletonLabel) + .deploy(abi.encode(deployedContracts.addressesRegistry)); + + deployedContracts.gasPool = deployer + .create3("GasPool.sol:GasPool") + .setLabel(cfg.singletonLabel) + .deploy(abi.encode(deployedContracts.addressesRegistry)); + + deployedContracts.collSurplusPool = deployer + .create3("CollSurplusPool.sol:CollSurplusPool") + .setLabel(cfg.singletonLabel) + .deploy(abi.encode(deployedContracts.addressesRegistry)); + + deployedContracts.sortedTroves = deployer + .create3("SortedTroves.sol:SortedTroves") + .setLabel(cfg.singletonLabel) + .deploy(abi.encode(deployedContracts.addressesRegistry)); + + assert( + deployedContracts.borrowerOperations == + precomputedAddresses.borrowerOperations + ); + assert( + deployedContracts.troveManager == precomputedAddresses.troveManager + ); + assert(deployedContracts.troveNFT == precomputedAddresses.troveNFT); + assert( + deployedContracts.stabilityPoolProxy == + precomputedAddresses.stabilityPoolProxy + ); + assert(deployedContracts.activePool == precomputedAddresses.activePool); + assert( + deployedContracts.defaultPool == precomputedAddresses.defaultPool + ); + assert(deployedContracts.gasPool == precomputedAddresses.gasPool); + assert( + deployedContracts.collSurplusPool == + precomputedAddresses.collSurplusPool + ); + assert( + deployedContracts.sortedTroves == precomputedAddresses.sortedTroves + ); + + _transferProxyAdminOwnerships(); + _verify(); + _previewNFT(); + } + + function _buildAddressVars() + internal + view + returns (IAddressesRegistry.AddressVars memory) + { + return + IAddressesRegistry.AddressVars({ + borrowerOperations: IBorrowerOperations( + precomputedAddresses.borrowerOperations + ), + troveManager: ITroveManager(precomputedAddresses.troveManager), + troveNFT: ITroveNFT(precomputedAddresses.troveNFT), + metadataNFT: IMetadataNFT(deployedContracts.metadataNFT), + stabilityPool: IStabilityPool( + precomputedAddresses.stabilityPoolProxy + ), + priceFeed: IPriceFeed(deployedContracts.priceFeedProxy), + activePool: IActivePool(precomputedAddresses.activePool), + defaultPool: IDefaultPool(precomputedAddresses.defaultPool), + gasPoolAddress: precomputedAddresses.gasPool, + collSurplusPool: ICollSurplusPool( + precomputedAddresses.collSurplusPool + ), + sortedTroves: ISortedTroves(precomputedAddresses.sortedTroves), + interestRouter: IInterestRouter(cfg.yieldSplitAddress), + hintHelpers: IHintHelpers(deployedContracts.hintHelpers), + multiTroveGetter: IMultiTroveGetter( + deployedContracts.multiTroveGetter + ), + collateralRegistry: ICollateralRegistry( + deployedContracts.collateralRegistry + ), + boldToken: IBoldToken(debtToken), + collToken: IERC20Metadata(collateralToken), + gasToken: IERC20Metadata(gasToken), + liquidityStrategy: cdpLiquidityStrategy + }); + } + + function _transferProxyAdminOwnerships() internal { + Ownable( + deployer.harness(getProxyAdmin(deployedContracts.priceFeedProxy)) + ).transferOwnership(cfg.owner); + Ownable( + deployer.harness( + getProxyAdmin(deployedContracts.stabilityPoolProxy) + ) + ).transferOwnership(cfg.owner); + Ownable( + deployer.harness(getProxyAdmin(deployedContracts.systemParamsProxy)) + ).transferOwnership(cfg.owner); + } + + function _deploySystemParams() internal { + ISystemParams.DebtParams memory debtParams = ISystemParams.DebtParams({ + minDebt: cfg.minDebt + }); + ISystemParams.LiquidationParams memory liquidationParams = ISystemParams + .LiquidationParams({ + liquidationPenaltySP: cfg.liquidationPenaltySP, + liquidationPenaltyRedistribution: cfg + .liquidationPenaltyRedistribution + }); + ISystemParams.GasCompParams memory gasCompParams = ISystemParams + .GasCompParams({ + collGasCompensationDivisor: cfg.collGasCompensationDivisor, + collGasCompensationCap: cfg.collGasCompensationCap, + ethGasCompensation: cfg.ethGasCompensation + }); + ISystemParams.CollateralParams memory collateralParams = ISystemParams + .CollateralParams({ + ccr: cfg.CCR, + scr: cfg.SCR, + mcr: cfg.MCR, + bcr: cfg.BCR + }); + ISystemParams.InterestParams memory interestParams = ISystemParams + .InterestParams({minAnnualInterestRate: cfg.minAnnualInterestRate}); + ISystemParams.RedemptionParams memory redemptionParams = ISystemParams + .RedemptionParams({ + redemptionFeeFloor: cfg.redemptionFeeFloor, + initialBaseRate: cfg.initialBaseRate, + redemptionMinuteDecayFactor: cfg.redemptionMinuteDecayFactor, + redemptionBeta: cfg.redemptionBeta + }); + ISystemParams.StabilityPoolParams memory poolParams = ISystemParams + .StabilityPoolParams({ + spYieldSplit: cfg.spYieldSplit, + minBoldInSP: cfg.minBoldInSP, + minBoldAfterRebalance: cfg.minBoldAfterRebalance + }); + + upgradeableContractsImplementations + .systemParamsImplementation = deployer + .create3("SystemParams.sol:SystemParams") + .setLabel(cfg.singletonLabel) + .deploy( + abi.encode( + true, // disableInitializers for implementation + debtParams, + liquidationParams, + gasCompParams, + collateralParams, + interestParams, + redemptionParams, + poolParams + ) + ); + + deployedContracts.systemParamsProxy = deployOztupProxy( + deployer, + string.concat("SystemParamsProxy:", cfg.proxyLabel), + upgradeableContractsImplementations.systemParamsImplementation, + "" + ); + } + + function _deployFXPriceFeed(address borrowerOperationsAddress) internal { + upgradeableContractsImplementations.fxPriceFeedImplementation = deployer + .create3("FXPriceFeed.sol:FXPriceFeed") + .setLabel(cfg.singletonLabel) + .deploy(abi.encode(true)); + + deployedContracts.priceFeedProxy = deployOztupProxy( + deployer, + string.concat("FXPriceFeedProxy:", cfg.proxyLabel), + upgradeableContractsImplementations.fxPriceFeedImplementation, + abi.encodeWithSelector( + FXPriceFeed.initialize.selector, + oracleAdapter, + cfg.rateFeedID, + cfg.invertRateFeed, + cfg.l2SequencerGracePeriod, + borrowerOperationsAddress, + cfg.watchdog, + cfg.owner + ) + ); + } + + function _deployCollateralRegistry() internal { + IERC20Metadata[] memory collaterals = new IERC20Metadata[](1); + collaterals[0] = IERC20Metadata(collateralToken); + + ITroveManager[] memory troveManagers = new ITroveManager[](1); + troveManagers[0] = ITroveManager(precomputedAddresses.troveManager); + + deployedContracts.collateralRegistry = deployer + .create3("CollateralRegistry.sol:CollateralRegistry") + .setLabel(cfg.singletonLabel) + .deploy( + abi.encode( + IBoldToken(address(debtToken)), + collaterals, + troveManagers, + ISystemParams(deployedContracts.systemParamsProxy), + cdpLiquidityStrategy + ) + ); + } + + // ── Verification ──────────────────────────────────────────────────────── + + function _verify() internal view { + IAddressesRegistry ar = IAddressesRegistry( + deployedContracts.addressesRegistry + ); + ICollateralRegistry cr = ICollateralRegistry( + deployedContracts.collateralRegistry + ); + ISystemParams sp = ISystemParams(deployedContracts.systemParamsProxy); + FXPriceFeed pf = FXPriceFeed(deployedContracts.priceFeedProxy); + + // ── AddressesRegistry wiring ───────────────────────────────────── + require( + address(ar.borrowerOperations()) == + deployedContracts.borrowerOperations, + "AR: borrowerOperations" + ); + require( + address(ar.troveManager()) == deployedContracts.troveManager, + "AR: troveManager" + ); + require( + address(ar.troveNFT()) == deployedContracts.troveNFT, + "AR: troveNFT" + ); + require( + address(ar.metadataNFT()) == deployedContracts.metadataNFT, + "AR: metadataNFT" + ); + require( + address(ar.stabilityPool()) == deployedContracts.stabilityPoolProxy, + "AR: stabilityPool" + ); + require( + address(ar.priceFeed()) == deployedContracts.priceFeedProxy, + "AR: priceFeed" + ); + require( + address(ar.activePool()) == deployedContracts.activePool, + "AR: activePool" + ); + require( + address(ar.defaultPool()) == deployedContracts.defaultPool, + "AR: defaultPool" + ); + require( + ar.gasPoolAddress() == deployedContracts.gasPool, + "AR: gasPool" + ); + require( + address(ar.collSurplusPool()) == deployedContracts.collSurplusPool, + "AR: collSurplusPool" + ); + require( + address(ar.sortedTroves()) == deployedContracts.sortedTroves, + "AR: sortedTroves" + ); + require( + address(ar.interestRouter()) == cfg.yieldSplitAddress, + "AR: interestRouter" + ); + require( + address(ar.hintHelpers()) == deployedContracts.hintHelpers, + "AR: hintHelpers" + ); + require( + address(ar.multiTroveGetter()) == + deployedContracts.multiTroveGetter, + "AR: multiTroveGetter" + ); + require( + address(ar.collateralRegistry()) == + deployedContracts.collateralRegistry, + "AR: collateralRegistry" + ); + require(address(ar.boldToken()) == debtToken, "AR: boldToken"); + require(address(ar.collToken()) == collateralToken, "AR: collToken"); + require(address(ar.gasToken()) == gasToken, "AR: gasToken"); + require( + ar.liquidityStrategy() == cdpLiquidityStrategy, + "AR: liquidityStrategy" + ); + + // ── CollateralRegistry wiring ──────────────────────────────────── + require(cr.totalCollaterals() == 1, "CR: totalCollaterals"); + require( + address(cr.getToken(0)) == collateralToken, + "CR: collateral[0]" + ); + require( + address(cr.getTroveManager(0)) == deployedContracts.troveManager, + "CR: troveManager[0]" + ); + require(address(cr.boldToken()) == debtToken, "CR: boldToken"); + require( + cr.liquidityStrategy() == cdpLiquidityStrategy, + "CR: liquidityStrategy" + ); + + // ── MetadataNFT wiring ────────────────────────────────────────── + MetadataNFT nft = MetadataNFT(deployedContracts.metadataNFT); + FixedAssetReader assetReader = nft.assetReader(); + require(address(assetReader) != address(0), "NFT: assetReader not set"); + require( + assetReader.pointer() != address(0), + "NFT: SSTORE2 pointer not set" + ); + require( + bytes(assetReader.readAsset(bytes4(keccak256("BOLD")))).length > 0, + "NFT: debt token logo asset empty" + ); + require( + bytes( + assetReader.readAsset( + bytes4(keccak256(bytes(cfg.collateralTokenSymbol))) + ) + ).length > 0, + "NFT: collateral logo asset empty" + ); + require( + bytes(assetReader.readAsset(bytes4(keccak256("geist")))).length > 0, + "NFT: font asset empty" + ); + + // ── FXPriceFeed proxy parameters ───────────────────────────────── + require( + address(pf.oracleAdapter()) == oracleAdapter, + "PF: oracleAdapter" + ); + require(pf.rateFeedID() == cfg.rateFeedID, "PF: rateFeedID"); + require( + pf.invertRateFeed() == cfg.invertRateFeed, + "PF: invertRateFeed" + ); + require( + pf.l2SequencerGracePeriod() == cfg.l2SequencerGracePeriod, + "PF: l2SequencerGracePeriod" + ); + require(pf.watchdogAddress() == cfg.watchdog, "PF: watchdog"); + require( + address(pf.borrowerOperations()) == + deployedContracts.borrowerOperations, + "PF: borrowerOperations" + ); + require( + Ownable(deployedContracts.priceFeedProxy).owner() == cfg.owner, + "PF: owner" + ); + + // ── SystemParams proxy parameters ──────────────────────────────── + require(sp.CCR() == cfg.CCR, "SP: CCR"); + require(sp.MCR() == cfg.MCR, "SP: MCR"); + require(sp.BCR() == cfg.BCR, "SP: BCR"); + require(sp.SCR() == cfg.SCR, "SP: SCR"); + require( + sp.LIQUIDATION_PENALTY_SP() == cfg.liquidationPenaltySP, + "SP: liquidationPenaltySP" + ); + require( + sp.LIQUIDATION_PENALTY_REDISTRIBUTION() == + cfg.liquidationPenaltyRedistribution, + "SP: liquidationPenaltyRedistribution" + ); + require(sp.MIN_DEBT() == cfg.minDebt, "SP: minDebt"); + require( + sp.COLL_GAS_COMPENSATION_DIVISOR() == + cfg.collGasCompensationDivisor, + "SP: collGasCompensationDivisor" + ); + require( + sp.COLL_GAS_COMPENSATION_CAP() == cfg.collGasCompensationCap, + "SP: collGasCompensationCap" + ); + require( + sp.ETH_GAS_COMPENSATION() == cfg.ethGasCompensation, + "SP: ethGasCompensation" + ); + require( + sp.MIN_ANNUAL_INTEREST_RATE() == cfg.minAnnualInterestRate, + "SP: minAnnualInterestRate" + ); + require( + sp.REDEMPTION_FEE_FLOOR() == cfg.redemptionFeeFloor, + "SP: redemptionFeeFloor" + ); + require( + sp.INITIAL_BASE_RATE() == cfg.initialBaseRate, + "SP: initialBaseRate" + ); + require( + sp.REDEMPTION_MINUTE_DECAY_FACTOR() == + cfg.redemptionMinuteDecayFactor, + "SP: redemptionMinuteDecayFactor" + ); + require( + sp.REDEMPTION_BETA() == cfg.redemptionBeta, + "SP: redemptionBeta" + ); + require(sp.SP_YIELD_SPLIT() == cfg.spYieldSplit, "SP: spYieldSplit"); + require(sp.MIN_BOLD_IN_SP() == cfg.minBoldInSP, "SP: minBoldInSP"); + require( + sp.MIN_BOLD_AFTER_REBALANCE() == cfg.minBoldAfterRebalance, + "SP: minBoldAfterRebalance" + ); + + // ── Proxy upgradeability: ProxyAdmin owner == cfg.owner ───────── + require( + Ownable(getProxyAdmin(deployedContracts.priceFeedProxy)).owner() == + cfg.owner, + "ProxyAdmin: priceFeed" + ); + require( + Ownable(getProxyAdmin(deployedContracts.stabilityPoolProxy)) + .owner() == cfg.owner, + "ProxyAdmin: stabilityPool" + ); + require( + Ownable(getProxyAdmin(deployedContracts.systemParamsProxy)) + .owner() == cfg.owner, + "ProxyAdmin: systemParams" + ); + } + + function _base64encode(string memory filePath) internal returns (bytes memory) { + string[] memory cmd = new string[](3); + cmd[0] = "bash"; + cmd[1] = "-c"; + cmd[2] = string.concat("base64 ", filePath, " | tr -d '\\n'"); + return vm.ffi(cmd); + } + + function _deployMetadata() internal { + string memory basePath = string.concat( + vm.projectRoot(), + "/", + cfg.metadataAssetsBasePath + ); + + // Load asset files (base64-encode SVGs at deploy time) + bytes memory debtTokenLogo = _base64encode( + string.concat(basePath, cfg.debtTokenLogoFile) + ); + bytes memory collateralLogo = _base64encode( + string.concat(basePath, cfg.collateralTokenLogoFile) + ); + bytes memory font = bytes( + vm.readFile(string.concat(basePath, cfg.fontFile)) + ); + + // Calculate byte offsets + uint128 debtLogoEnd = uint128(debtTokenLogo.length); + uint128 collLogoEnd = debtLogoEnd + uint128(collateralLogo.length); + uint128 fontEnd = collLogoEnd + uint128(font.length); + + // Concatenate all data + bytes memory allData = bytes.concat( + debtTokenLogo, + collateralLogo, + font + ); + + // Deploy SSTORE2DataPointer which calls SSTORE2.write in its constructor + address dataPointerContract = deployer + .create3("SSTORE2DataPointer.sol:SSTORE2DataPointer") + .setLabel(cfg.singletonLabel) + .deploy(abi.encode(allData)); + address pointer = SSTORE2DataPointer(dataPointerContract).pointer(); + + // Deploy FixedAssetReader via create3 + bytes4[] memory sigs = new bytes4[](3); + sigs[0] = bytes4(keccak256("BOLD")); + sigs[1] = bytes4(keccak256(bytes(cfg.collateralTokenSymbol))); + sigs[2] = bytes4(keccak256("geist")); + + FixedAssetReader.Asset[] + memory metadataAssets = new FixedAssetReader.Asset[](3); + metadataAssets[0] = FixedAssetReader.Asset(0, debtLogoEnd); + metadataAssets[1] = FixedAssetReader.Asset(debtLogoEnd, collLogoEnd); + metadataAssets[2] = FixedAssetReader.Asset(collLogoEnd, fontEnd); + + address fixedAssetReader = deployer + .create3("FixedAssets.sol:FixedAssetReader") + .setLabel(cfg.singletonLabel) + .deploy(abi.encode(pointer, sigs, metadataAssets)); + + // Deploy MetadataNFT via create3 + deployedContracts.metadataNFT = deployer + .create3("MetadataNFT.sol:MetadataNFT") + .setLabel(cfg.singletonLabel) + .deploy(abi.encode(FixedAssetReader(fixedAssetReader))); + } + + function _predict( + string memory artifact, + Senders.Sender storage _deployer, + string memory label + ) internal returns (address) { + return _deployer.create3(artifact).setLabel(label).predict(); + } + + function _previewNFT() internal { + IMetadataNFT.TroveData memory troveData; + troveData._tokenId = 1; + troveData._owner = address(0xBEEF); + troveData._collToken = collateralToken; + troveData._boldToken = debtToken; + troveData._collAmount = 10e18; + troveData._debtAmount = 5000e18; + troveData._interestRate = 5e16; // 5% + troveData._status = ITroveManager.Status.active; + + string memory dataURI = MetadataNFT(deployedContracts.metadataNFT).uri( + troveData + ); + + // Strip "data:application/json;base64," prefix (29 chars) and decode + bytes memory jsonBytes = Base64.decode(_substring(dataURI, 29)); + string memory json = string(jsonBytes); + + // Extract SVG image from metadata and write to out/ + string memory imageDataURI = abi.decode(vm.parseJson(json, ".image"), (string)); + // Strip "data:image/svg+xml;base64," prefix (26 chars) and decode + bytes memory svgBytes = Base64.decode(_substring(imageDataURI, 26)); + + string memory svgPath = string.concat("out/nft-preview-", cfg.proxyLabel, ".svg"); + vm.writeFile(svgPath, string(svgBytes)); + console2.log("=== NFT preview SVG written to: %s ===", svgPath); + } + + function _substring( + string memory str, + uint256 startIndex + ) internal pure returns (string memory) { + bytes memory strBytes = bytes(str); + bytes memory result = new bytes(strBytes.length - startIndex); + for (uint256 i = startIndex; i < strBytes.length; i++) { + result[i - startIndex] = strBytes[i]; + } + return string(result); + } +} diff --git a/script/helpers/AddressbookHelper.sol b/script/helpers/AddressbookHelper.sol deleted file mode 100644 index b4cbde64..00000000 --- a/script/helpers/AddressbookHelper.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import {TrebScript} from "lib/treb-sol/src/TrebScript.sol"; - -contract AddressbookHelper is TrebScript { - string private json; - string private network; - - constructor() { - network = vm.envString("NETWORK"); - try vm.readFile(".treb/addressbook.json") returns (string memory _json) { - json = _json; - } catch { - revert("AddressbookHelper: failed to load .treb/addressbook.json"); - } - } - - function lookupAddressbook(string memory _identifier) internal view returns (address) { - string memory jsonPath = string.concat(".", network, "[\"", _identifier, "\"]"); - try vm.parseJsonAddress(json, jsonPath) returns (address result) { - return result; - } catch { - revert(string.concat("AddressbookHelper: '", _identifier, "' not found in network '", network, "'")); - } - } -} diff --git a/script/helpers/SSTORE2DataPointer.sol b/script/helpers/SSTORE2DataPointer.sol new file mode 100644 index 00000000..20ff7497 --- /dev/null +++ b/script/helpers/SSTORE2DataPointer.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8; + +import "Solady/utils/SSTORE2.sol"; + +/// @notice Wrapper that calls SSTORE2.write in its constructor, +/// making the SSTORE2 data deployable via create3. +contract SSTORE2DataPointer { + address public immutable pointer; + + constructor(bytes memory _data) { + pointer = SSTORE2.write(_data); + } +}