diff --git a/contracts/lib/forge-std b/contracts/lib/forge-std index 2cbff0602..978ac6fad 160000 --- a/contracts/lib/forge-std +++ b/contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit 2cbff0602d340503dba9828ab6981053704d1384 +Subproject commit 978ac6fadb62f5f0b723c996f64be52eddba6801 diff --git a/contracts/src/AddressesRegistry.sol b/contracts/src/AddressesRegistry.sol index 644176f6d..e2c473eb0 100644 --- a/contracts/src/AddressesRegistry.sol +++ b/contracts/src/AddressesRegistry.sol @@ -4,7 +4,7 @@ import "./Dependencies/Ownable.sol"; import "./Interfaces/IAddressesRegistry.sol"; contract AddressesRegistry is Ownable, IAddressesRegistry { - IERC20 public collToken; + IERC20Metadata public collToken; IBorrowerOperations public borrowerOperations; ITroveManager public troveManager; ITroveNFT public troveNFT; diff --git a/contracts/src/CollateralRegistry.sol b/contracts/src/CollateralRegistry.sol index 9ddb09748..c5eee2221 100644 --- a/contracts/src/CollateralRegistry.sol +++ b/contracts/src/CollateralRegistry.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.18; -import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "./Interfaces/ITroveManager.sol"; import "./Interfaces/IBoldToken.sol"; @@ -18,16 +18,16 @@ contract CollateralRegistry is LiquityBase, ICollateralRegistry { // See: https://github.com/ethereum/solidity/issues/12587 uint256 public immutable totalCollaterals; - IERC20 internal immutable token0; - IERC20 internal immutable token1; - IERC20 internal immutable token2; - IERC20 internal immutable token3; - IERC20 internal immutable token4; - IERC20 internal immutable token5; - IERC20 internal immutable token6; - IERC20 internal immutable token7; - IERC20 internal immutable token8; - IERC20 internal immutable token9; + IERC20Metadata internal immutable token0; + IERC20Metadata internal immutable token1; + IERC20Metadata internal immutable token2; + IERC20Metadata internal immutable token3; + IERC20Metadata internal immutable token4; + IERC20Metadata internal immutable token5; + IERC20Metadata internal immutable token6; + IERC20Metadata internal immutable token7; + IERC20Metadata internal immutable token8; + IERC20Metadata internal immutable token9; // TODO: make this immutable again ITroveManager[10] public troveManagers; @@ -42,7 +42,7 @@ contract CollateralRegistry is LiquityBase, ICollateralRegistry { event BaseRateUpdated(uint256 _baseRate); event LastFeeOpTimeUpdated(uint256 _lastFeeOpTime); - constructor(IBoldToken _boldToken, IERC20[] memory _tokens) { + constructor(IBoldToken _boldToken, IERC20Metadata[] memory _tokens) { uint256 numTokens = _tokens.length; require(numTokens > 0, "Collateral list cannot be empty"); require(numTokens < 10, "Collateral list too long"); @@ -51,15 +51,15 @@ contract CollateralRegistry is LiquityBase, ICollateralRegistry { boldToken = _boldToken; token0 = _tokens[0]; - token1 = numTokens > 1 ? _tokens[1] : IERC20(address(0)); - token2 = numTokens > 2 ? _tokens[2] : IERC20(address(0)); - token3 = numTokens > 3 ? _tokens[3] : IERC20(address(0)); - token4 = numTokens > 4 ? _tokens[4] : IERC20(address(0)); - token5 = numTokens > 5 ? _tokens[5] : IERC20(address(0)); - token6 = numTokens > 6 ? _tokens[6] : IERC20(address(0)); - token7 = numTokens > 7 ? _tokens[7] : IERC20(address(0)); - token8 = numTokens > 8 ? _tokens[8] : IERC20(address(0)); - token9 = numTokens > 9 ? _tokens[9] : IERC20(address(0)); + token1 = numTokens > 1 ? _tokens[1] : IERC20Metadata(address(0)); + token2 = numTokens > 2 ? _tokens[2] : IERC20Metadata(address(0)); + token3 = numTokens > 3 ? _tokens[3] : IERC20Metadata(address(0)); + token4 = numTokens > 4 ? _tokens[4] : IERC20Metadata(address(0)); + token5 = numTokens > 5 ? _tokens[5] : IERC20Metadata(address(0)); + token6 = numTokens > 6 ? _tokens[6] : IERC20Metadata(address(0)); + token7 = numTokens > 7 ? _tokens[7] : IERC20Metadata(address(0)); + token8 = numTokens > 8 ? _tokens[8] : IERC20Metadata(address(0)); + token9 = numTokens > 9 ? _tokens[9] : IERC20Metadata(address(0)); // Initialize the baseRate state variable baseRate = INITIAL_BASE_RATE; @@ -252,7 +252,7 @@ contract CollateralRegistry is LiquityBase, ICollateralRegistry { // getters - function getToken(uint256 _index) external view returns (IERC20) { + function getToken(uint256 _index) external view returns (IERC20Metadata) { if (_index == 0) return token0; else if (_index == 1) return token1; else if (_index == 2) return token2; diff --git a/contracts/src/Interfaces/IAddressesRegistry.sol b/contracts/src/Interfaces/IAddressesRegistry.sol index a87f55e64..797961fdf 100644 --- a/contracts/src/Interfaces/IAddressesRegistry.sol +++ b/contracts/src/Interfaces/IAddressesRegistry.sol @@ -17,7 +17,7 @@ import "./IPriceFeed.sol"; interface IAddressesRegistry { struct AddressVars { - IERC20 collToken; + IERC20Metadata collToken; IBorrowerOperations borrowerOperations; ITroveManager troveManager; ITroveNFT troveNFT; @@ -42,7 +42,7 @@ interface IAddressesRegistry { function LIQUIDATION_PENALTY_SP() external returns (uint256); function LIQUIDATION_PENALTY_REDISTRIBUTION() external returns (uint256); - function collToken() external view returns (IERC20); + function collToken() external view returns (IERC20Metadata); function borrowerOperations() external view returns (IBorrowerOperations); function troveManager() external view returns (ITroveManager); function troveNFT() external view returns (ITroveNFT); diff --git a/contracts/src/Interfaces/ICollateralRegistry.sol b/contracts/src/Interfaces/ICollateralRegistry.sol index 09e5d99e7..ccc4eb413 100644 --- a/contracts/src/Interfaces/ICollateralRegistry.sol +++ b/contracts/src/Interfaces/ICollateralRegistry.sol @@ -1,6 +1,6 @@ pragma solidity ^0.8.0; -import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "./IBoldToken.sol"; import "./ITroveManager.sol"; @@ -13,7 +13,7 @@ interface ICollateralRegistry { function redeemCollateral(uint256 _boldamount, uint256 _maxIterations, uint256 _maxFeePercentage) external; // getters function totalCollaterals() external view returns (uint256); - function getToken(uint256 _index) external view returns (IERC20); + function getToken(uint256 _index) external view returns (IERC20Metadata); function troveManagers(uint256 _index) external view returns (ITroveManager); function boldToken() external view returns (IBoldToken); diff --git a/contracts/src/Interfaces/ITroveNFT.sol b/contracts/src/Interfaces/ITroveNFT.sol index fcfce74e8..613b968f0 100644 --- a/contracts/src/Interfaces/ITroveNFT.sol +++ b/contracts/src/Interfaces/ITroveNFT.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.0; -import "openzeppelin-contracts/contracts/token/ERC721/IERC721.sol"; +import "openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Metadata.sol"; import "./ITroveManager.sol"; -interface ITroveNFT is IERC721 { +interface ITroveNFT is IERC721Metadata { function mint(address _owner, uint256 _troveId) external; function burn(uint256 _troveId) external; } diff --git a/contracts/src/TroveNFT.sol b/contracts/src/TroveNFT.sol index e774613dd..fdd7dab6a 100644 --- a/contracts/src/TroveNFT.sol +++ b/contracts/src/TroveNFT.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.18; import "openzeppelin-contracts/contracts/token/ERC721/ERC721.sol"; +import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; import "./Interfaces/ITroveNFT.sol"; import "./Interfaces/IAddressesRegistry.sol"; @@ -10,13 +11,16 @@ import "./Interfaces/IAddressesRegistry.sol"; // import "forge-std/console2.sol"; contract TroveNFT is ERC721, ITroveNFT { - string internal constant NAME = "TroveNFT"; // TODO - string internal constant SYMBOL = "Lv2T"; // TODO ITroveManager public troveManager; + IERC20 internal immutable collToken; - constructor(IAddressesRegistry _addressesRegistry) ERC721(NAME, SYMBOL) { + constructor(IAddressesRegistry _addressesRegistry) ERC721( + string.concat("Liquity v2 Trove - ", _addressesRegistry.collToken().name()), + string.concat("Lv2T_", _addressesRegistry.collToken().symbol()) + ) { troveManager = _addressesRegistry.troveManager(); + collToken = _addressesRegistry.collToken(); } function mint(address _owner, uint256 _troveId) external override { diff --git a/contracts/src/scripts/DeployLiquity2.s.sol b/contracts/src/scripts/DeployLiquity2.s.sol index 24423f13a..2cb318faf 100644 --- a/contracts/src/scripts/DeployLiquity2.s.sol +++ b/contracts/src/scripts/DeployLiquity2.s.sol @@ -45,7 +45,7 @@ contract DeployLiquity2Script is Script, StdCheats { IPriceFeedTestnet priceFeed; // Tester GasPool gasPool; IInterestRouter interestRouter; - IERC20 collToken; + IERC20Metadata collToken; } struct LiquityContractAddresses { @@ -72,7 +72,7 @@ contract DeployLiquity2Script is Script, StdCheats { struct DeploymentVarsTestnet { uint256 numCollaterals; - IERC20[] collaterals; + IERC20Metadata[] collaterals; LiquityContractsTestnet contracts; bytes bytecode; address boldTokenAddress; @@ -153,7 +153,7 @@ contract DeployLiquity2Script is Script, StdCheats { vm.startBroadcast(trove.owner); // Approve collToken to BorrowerOperations - IERC20(contracts.collToken).approve( + IERC20Metadata(contracts.collToken).approve( address(contracts.borrowerOperations), trove.coll + ETH_GAS_COMPENSATION ); @@ -199,12 +199,12 @@ contract DeployLiquity2Script is Script, StdCheats { assert(address(boldToken) == vars.boldTokenAddress); contractsArray = new LiquityContractsTestnet[](vars.numCollaterals); - vars.collaterals = new IERC20[](vars.numCollaterals); + vars.collaterals = new IERC20Metadata[](vars.numCollaterals); // Deploy the first branch with WETH collateral vars.collaterals[0] = _WETH; for (vars.i = 1; vars.i < vars.numCollaterals; vars.i++) { - IERC20 collToken = new ERC20Faucet( + IERC20Metadata collToken = new ERC20Faucet( string.concat("Staked ETH", string(abi.encode(vars.i))), // _name string.concat("stETH", string(abi.encode(vars.i))), // _symbol 100 ether, // _tapAmount @@ -242,7 +242,7 @@ contract DeployLiquity2Script is Script, StdCheats { function _deployAndConnectCollateralContractsTestnet( uint256 _branch, - IERC20 _collToken, + IERC20Metadata _collToken, IBoldToken _boldToken, ICollateralRegistry _collateralRegistry, IWETH _weth, diff --git a/contracts/src/test/TestContracts/CollateralRegistryTester.sol b/contracts/src/test/TestContracts/CollateralRegistryTester.sol index 20e9db1d2..918838dbe 100644 --- a/contracts/src/test/TestContracts/CollateralRegistryTester.sol +++ b/contracts/src/test/TestContracts/CollateralRegistryTester.sol @@ -8,7 +8,7 @@ import "../../CollateralRegistry.sol"; for testing the parent's internal functions. */ contract CollateralRegistryTester is CollateralRegistry { - constructor(IBoldToken _boldToken, IERC20[] memory _tokens) CollateralRegistry(_boldToken, _tokens) {} + constructor(IBoldToken _boldToken, IERC20Metadata[] memory _tokens) CollateralRegistry(_boldToken, _tokens) {} function unprotectedDecayBaseRateFromBorrowing() external returns (uint256) { baseRate = _calcDecayedBaseRate(); diff --git a/contracts/src/test/TestContracts/Deployment.t.sol b/contracts/src/test/TestContracts/Deployment.t.sol index 54121e63a..1ebca7f40 100644 --- a/contracts/src/test/TestContracts/Deployment.t.sol +++ b/contracts/src/test/TestContracts/Deployment.t.sol @@ -47,7 +47,7 @@ contract TestDeployer { IPriceFeedTestnet priceFeed; // Tester GasPool gasPool; IInterestRouter interestRouter; - IERC20 collToken; + IERC20Metadata collToken; } struct LiquityContracts { @@ -63,7 +63,7 @@ contract TestDeployer { IPriceFeed priceFeed; GasPool gasPool; IInterestRouter interestRouter; - IERC20 collToken; + IERC20Metadata collToken; } struct LiquityContractAddresses { @@ -90,7 +90,7 @@ contract TestDeployer { struct DeploymentVarsDev { uint256 numCollaterals; - IERC20[] collaterals; + IERC20Metadata[] collaterals; LiquityContractsDev contracts; bytes bytecode; address boldTokenAddress; @@ -114,7 +114,7 @@ contract TestDeployer { struct DeploymentVarsMainnet { uint256 numCollaterals; - IERC20[] collaterals; + IERC20Metadata[] collaterals; IPriceFeed[] priceFeeds; bytes bytecode; address boldTokenAddress; @@ -144,10 +144,10 @@ contract TestDeployer { // TODO: replace this with the real LST contracts struct MockCollaterals { IWETH WETH; - IERC20 RETH; - IERC20 WSTETH; - IERC20 ETHX; - IERC20 OSETH; + IERC20Metadata RETH; + IERC20Metadata WSTETH; + IERC20Metadata ETHX; + IERC20Metadata OSETH; } // See: https://solidity-by-example.org/app/create2/ @@ -236,12 +236,12 @@ contract TestDeployer { assert(address(boldToken) == vars.boldTokenAddress); contractsArray = new LiquityContractsDev[](vars.numCollaterals); - vars.collaterals = new IERC20[](vars.numCollaterals); + vars.collaterals = new IERC20Metadata[](vars.numCollaterals); // Deploy the first branch with WETH collateral vars.collaterals[0] = _WETH; for (vars.i = 1; vars.i < vars.numCollaterals; vars.i++) { - IERC20 collToken = new ERC20Faucet( + IERC20Metadata collToken = new ERC20Faucet( string.concat("Staked ETH", string(abi.encode(vars.i))), // _name string.concat("stETH", string(abi.encode(vars.i))), // _symbol 100 ether, // _tapAmount @@ -287,7 +287,7 @@ contract TestDeployer { vars.numCollaterals = 5; result.contractsArray = new LiquityContracts[](vars.numCollaterals); vars.priceFeeds = new IPriceFeed[](vars.numCollaterals); - vars.collaterals = new IERC20[](vars.numCollaterals); + vars.collaterals = new IERC20Metadata[](vars.numCollaterals); vars.priceFeeds[0] = new WETHPriceFeed( address(this), _params.externalAddresses.ETHOracle, _params.oracleParams.ethUsdStalenessThreshold @@ -399,7 +399,7 @@ contract TestDeployer { function _deployAndConnectCollateralContractsDev( uint256 _branch, - IERC20 _collToken, + IERC20Metadata _collToken, IBoldToken _boldToken, ICollateralRegistry _collateralRegistry, IWETH _weth, @@ -507,7 +507,7 @@ contract TestDeployer { function _deployAndConnectCollateralContractsMainnet( uint256 _branch, - IERC20 _collToken, + IERC20Metadata _collToken, IPriceFeed _priceFeed, IBoldToken _boldToken, ICollateralRegistry _collateralRegistry, diff --git a/contracts/src/test/troveNFT.t.sol b/contracts/src/test/troveNFT.t.sol new file mode 100644 index 000000000..b219dbaf6 --- /dev/null +++ b/contracts/src/test/troveNFT.t.sol @@ -0,0 +1,19 @@ +pragma solidity 0.8.18; + +import "./TestContracts/DevTestSetup.sol"; + +contract TroveManagerTest is DevTestSetup { + function testTroveNFTMetadata() public { + priceFeed.setPrice(2000e18); + + vm.startPrank(A); + borrowerOperations.openTrove( + A, 0, 2e18, 2000e18, 0, 0, MIN_ANNUAL_INTEREST_RATE, 1000e18, address(0), address(0), address(0) + ); + + IERC721Metadata troveNFT = troveManager.troveNFT(); + + assertEq(troveNFT.name(), "Liquity v2 Trove - Wrapped Ether Tester", "Invalid Trove Name"); + assertEq(troveNFT.symbol(), "Lv2T_WETH", "Invalid Trove Symbol"); + } +}