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);
+ }
+}