diff --git a/foundry.toml b/foundry.toml index 586198a..1b4270a 100644 --- a/foundry.toml +++ b/foundry.toml @@ -9,7 +9,7 @@ gas_reports = ["*"] via_ir = false sizes = true optimizer_runs=1000 -solc_version = '0.8.22' +solc_version = '0.8.23' fs_permissions = [{ access = "read-write", path = "./scripts/foundry/transaction.json"}, { access = "read-write", path = "./scripts/foundry/transactions.json"}, { access = "read-write", path = "./lib/angle-tokens/scripts"}, { access = "read-write", path = "./scripts/proposals/transmuter/selectors.json"}, { access = "read-write", path = "./scripts/foundry/transmuter/updateFacets/selectors_replace.json"}, { access = "read-write", path = "./scripts/foundry/transmuter/updateFacets/selectors_add.json"}] ffi = true diff --git a/helpers/createTx.sh b/helpers/createTx.sh index b757933..71cf2e2 100644 --- a/helpers/createTx.sh +++ b/helpers/createTx.sh @@ -120,7 +120,7 @@ function main { read execute if [[ $execute == "yes" ]]; then - yarn submit:foundry + bun submit:foundry fi done } diff --git a/lib/angle-transmuter b/lib/angle-transmuter index 41d56b5..3ea9f59 160000 --- a/lib/angle-transmuter +++ b/lib/angle-transmuter @@ -1 +1 @@ -Subproject commit 41d56b5fad0fc1582249c22e14a7206a84fa471c +Subproject commit 3ea9f59d4de1b1812ca50d960ff6afc0e21d0573 diff --git a/lib/borrow-contracts b/lib/borrow-contracts index ffd3a76..f88d770 160000 --- a/lib/borrow-contracts +++ b/lib/borrow-contracts @@ -1 +1 @@ -Subproject commit ffd3a76067d3265b3631bc61bb9406704bb17eb9 +Subproject commit f88d7703846f04b7821192bcdaf1093d6f24735d diff --git a/lib/forge-std b/lib/forge-std index 52715a2..0e70977 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 52715a217dc51d0de15877878ab8213f6cbbbab5 +Subproject commit 0e7097750918380d84dd3cfdef595bee74dabb70 diff --git a/lib/merkl-contracts b/lib/merkl-contracts index 9890715..a702097 160000 --- a/lib/merkl-contracts +++ b/lib/merkl-contracts @@ -1 +1 @@ -Subproject commit 9890715728874b28008b15fe4fbe98dcf7bb3e5f +Subproject commit a7020975374c95d15c48791e7f77acd1065f3eb6 diff --git a/lib/safe-contracts b/lib/safe-contracts index 8ffae95..129f02c 160000 --- a/lib/safe-contracts +++ b/lib/safe-contracts @@ -1 +1 @@ -Subproject commit 8ffae95faa815acf86ec8b50021ebe9f96abde10 +Subproject commit 129f02cdd72b7af806d90a622959f42d6171bd70 diff --git a/package.json b/package.json index 2734d2b..ad1627f 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "prettier": "prettier --write ." }, "devDependencies": { - "@angleprotocol/sdk": "^v2.28.19", + "@angleprotocol/sdk": "^v2.33.34", "@chainlink/contracts": "0.2.1", "@metamask/detect-provider": "1.2.0", "@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers", @@ -59,4 +59,4 @@ "keywords": [], "author": "", "license": "ISC" -} +} \ No newline at end of file diff --git a/scripts/foundry/router/UpgradeRouter.s.sol b/scripts/foundry/router/UpgradeRouter.s.sol index 0bcc24f..5858e2e 100644 --- a/scripts/foundry/router/UpgradeRouter.s.sol +++ b/scripts/foundry/router/UpgradeRouter.s.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.19; -import {TransparentUpgradeableProxy} from "oz/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {ITransparentUpgradeableProxy} from "oz/proxy/transparent/TransparentUpgradeableProxy.sol"; import { MultiSend, Utils } from "../Utils.s.sol"; import { Enum } from "safe/Safe.sol"; import "../Constants.s.sol"; @@ -21,7 +21,7 @@ contract UpgradeRouter is Utils { if (chainId == CHAIN_LINEA) { { - bytes memory data = abi.encodeWithSelector(TransparentUpgradeableProxy.upgradeTo.selector, routerImpl); + bytes memory data = abi.encodeWithSelector(ITransparentUpgradeableProxy.upgradeTo.selector, routerImpl); address to = router; bytes memory internalTx = abi.encodePacked(isDelegateCall, to, value, data.length, data); transactions = abi.encodePacked(transactions, internalTx); diff --git a/scripts/foundry/transaction.json b/scripts/foundry/transaction.json index 095ec12..9178da0 100644 --- a/scripts/foundry/transaction.json +++ b/scripts/foundry/transaction.json @@ -1,8 +1,9 @@ { - "additionalData": "0x89e1e82a0000000000000000000000000000000000000000000000000000000000000000", - "chainId": 8453, - "data": "0x8d80ff0a00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000079003ef3d8ba38ebe18db133cec108f4d14ce00dd9ae0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002489e1e82a000000000000000000000000000000000000000000000000000000000000000000000000000000", + "additionalData": "0x", + "chainId": 1, + "data": "0x8d80ff0a000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001b200222222fd79264bbe280b4986f6fefbc3524d0137000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000845c3eebda0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ca30c93b02514f86d5c86a6e375e3a330b435fb5000000000000000000000000a9ddd91249dfdd450e81e1c56ab60e1a62651701000000000000000000000000000000000000000000000011566924abcbc3d5eb00222222fd79264bbe280b4986f6fefbc3524d0137000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000845c3eebda00000000000000000000000000000000000000000000000000000000000000000000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0000000000000000000000000a9ddd91249dfdd450e81e1c56ab60e1a6265170100000000000000000000000000000000000000000000000026cc04d97b7c69610000000000000000000000000000", "operation": 1, - "to": "0xA1dabEF33b3B82c7814B6D82A79e50F4AC44102B", + "safe": "0xdC4e6DFe07EFCa50a197DF15D9200883eF4Eb1c8", + "to": "0x40A2aCCbd92BCA938b02010E17A5b8929b49130D", "value": 0 } \ No newline at end of file diff --git a/scripts/foundry/transmuter/TransmuterAddCollateralEURCV.s.sol b/scripts/foundry/transmuter/TransmuterAddCollateralEURCV.s.sol new file mode 100644 index 0000000..2bfc45a --- /dev/null +++ b/scripts/foundry/transmuter/TransmuterAddCollateralEURCV.s.sol @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.19; + +import { console } from "forge-std/console.sol"; +import "transmuter/transmuter/Storage.sol" as Storage; +import { ITransmuter, ISettersGovernor, ISettersGuardian, ISwapper } from "transmuter/interfaces/ITransmuter.sol"; +import { Enum } from "safe/Safe.sol"; +import { MultiSend, Utils } from "../Utils.s.sol"; +import "../Constants.s.sol"; + +contract TransmuterAddCollateralEURCV is Utils { + address public constant COLLATERAL_TO_ADD = 0x5F7827FDeb7c20b443265Fc2F40845B715385Ff2; + + bytes oracleConfigCollatToAdd; + uint64[] public xFeeMint; + int64[] public yFeeMint; + uint64[] public xFeeBurn; + int64[] public yFeeBurn; + address public agToken; + + function run() external { + uint256 chainId = vm.envUint("CHAIN_ID"); + + address safe = _chainToContract(chainId, ContractType.GovernorMultisig); + ITransmuter transmuter = ITransmuter(_chainToContract(chainId, ContractType.TransmuterAgEUR)); + agToken = address(transmuter.agToken()); + bytes memory transactions; + + uint64[] memory xFeeBurn = new uint64[](1); + uint64[] memory xFeeMint = new uint64[](3); + int64[] memory yFeeMint = new int64[](xFeeMint.length); + int64[] memory yFeeBurn = new int64[](xFeeBurn.length); + xFeeBurn[0] = 1e9; + yFeeBurn[0] = 0.0005e9; + + xFeeMint[0] = 0; + xFeeMint[1] = 0.29e9; + xFeeMint[2] = 0.30e9; + yFeeMint[0] = 0; + yFeeMint[1] = 0; + yFeeMint[2] = 100e9 - 1; + + // Add the new collateral + { + address to = address(transmuter); + uint8 isDelegateCall = 0; + { + bytes memory readData; + bytes memory targetData; + oracleConfigCollatToAdd = abi.encode( + Storage.OracleReadType.NO_ORACLE, + Storage.OracleReadType.STABLE, + readData, + targetData, + // With no oracle the below oracles are useless + abi.encode(uint128(0), uint128(0)) + ); + } + { + bytes memory data = abi.encodeWithSelector(ISettersGovernor.addCollateral.selector, COLLATERAL_TO_ADD); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, uint256(0), dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + { + // Mint fees + bytes memory data = abi.encodeWithSelector( + ISettersGuardian.setFees.selector, + COLLATERAL_TO_ADD, + xFeeMint, + yFeeMint, + true + ); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, uint256(0), dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + { + // Burn fees + bytes memory data = abi.encodeWithSelector( + ISettersGuardian.setFees.selector, + COLLATERAL_TO_ADD, + xFeeBurn, + yFeeBurn, + false + ); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, uint256(0), dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + { + bytes memory data = abi.encodeWithSelector( + ISettersGovernor.setOracle.selector, + COLLATERAL_TO_ADD, + oracleConfigCollatToAdd + ); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, uint256(0), dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + { + bytes memory data = abi.encodeWithSelector( + ISettersGuardian.togglePause.selector, + COLLATERAL_TO_ADD, + Storage.ActionType.Mint + ); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, uint256(0), dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + { + bytes memory data = abi.encodeWithSelector( + ISettersGuardian.togglePause.selector, + COLLATERAL_TO_ADD, + Storage.ActionType.Burn + ); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, uint256(0), dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + } + + bytes memory payloadMultiSend = abi.encodeWithSelector(MultiSend.multiSend.selector, transactions); + _serializeJson(chainId, address(_chainToMultiSend(chainId)), uint256(0), payloadMultiSend, Enum.Operation.DelegateCall, hex"", safe); + } +} diff --git a/scripts/foundry/transmuter/TransmuterAddCollateralMWEURC.s.sol b/scripts/foundry/transmuter/TransmuterAddCollateralMWEURC.s.sol new file mode 100644 index 0000000..6cfc3e1 --- /dev/null +++ b/scripts/foundry/transmuter/TransmuterAddCollateralMWEURC.s.sol @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.19; + +import { console } from "forge-std/console.sol"; +import "transmuter/transmuter/Storage.sol" as Storage; +import { ITransmuter, ISettersGovernor, ISettersGuardian, ISwapper } from "transmuter/interfaces/ITransmuter.sol"; +import { Enum } from "safe/Safe.sol"; +import { MultiSend, Utils } from "../Utils.s.sol"; +import { MAX_MINT_FEE, MAX_BURN_FEE } from "transmuter/utils/Constants.sol"; +import "../Constants.s.sol"; + +contract TransmuterAddCollateralMWEURC is Utils { + address public constant COLLATERAL_TO_ADD = 0xf24608E0CCb972b0b0f4A6446a0BBf58c701a026; + + bytes oracleConfigCollatToAdd; + uint64[] public xFeeMint; + int64[] public yFeeMint; + uint64[] public xFeeBurn; + int64[] public yFeeBurn; + uint256 public capStablecoin; + address public agToken; + + function run() external { + uint256 chainId = vm.envUint("CHAIN_ID"); + + ITransmuter transmuter = ITransmuter(_chainToContract(chainId, ContractType.TransmuterAgEUR)); + agToken = address(transmuter.agToken()); + bytes memory transactions; + uint8 isDelegateCall = 0; + address to = address(transmuter); + uint256 value = 0; + + xFeeBurn = new uint64[](3); + xFeeMint = new uint64[](3); + yFeeMint = new int64[](xFeeMint.length); + yFeeBurn = new int64[](xFeeBurn.length); + xFeeBurn[0] = 1e9; + xFeeBurn[1] = 0.21e9; + xFeeBurn[2] = 0.20e9; + yFeeBurn[0] = 0.005e9; + yFeeBurn[1] = 0.005e9; + yFeeBurn[2] = int64(uint64(MAX_BURN_FEE)); + + xFeeMint[0] = 0; + xFeeMint[1] = 0.59e9; + xFeeMint[2] = 0.60e9; + yFeeMint[0] = 0.0005e9; + yFeeMint[1] = 0.0005e9; + yFeeMint[2] = int64(uint64(MAX_MINT_FEE)); + + capStablecoin = 2_000_000 ether; + + // Add the new collateral + { + { + bytes memory data = abi.encodeWithSelector(ISettersGovernor.addCollateral.selector, COLLATERAL_TO_ADD); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, value, dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + { + // Mint fees + bytes memory data = abi.encodeWithSelector( + ISettersGuardian.setFees.selector, + COLLATERAL_TO_ADD, + xFeeMint, + yFeeMint, + true + ); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, value, dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + { + // Burn fees + bytes memory data = abi.encodeWithSelector( + ISettersGuardian.setFees.selector, + COLLATERAL_TO_ADD, + xFeeBurn, + yFeeBurn, + false + ); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, value, dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + + { + address oracle = 0xa7ea0d40C246b876F76713Ba9a9A95f3f18AB794; + uint256 normalizationFactor = 1e18; + bytes memory targetData = abi.encode(1000794000000000000); // 1008235463728948111 + bytes memory readData = abi.encode(oracle, normalizationFactor); + oracleConfigCollatToAdd = abi.encode( + Storage.OracleReadType.MORPHO_ORACLE, + Storage.OracleReadType.MAX, + readData, + targetData, + abi.encode(uint128(0), uint128(0.0005e18)) + ); + } + + { + bytes memory data = abi.encodeWithSelector( + ISettersGovernor.setOracle.selector, + COLLATERAL_TO_ADD, + oracleConfigCollatToAdd + ); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, value, dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + { + bytes memory data = abi.encodeWithSelector( + ISettersGuardian.togglePause.selector, + COLLATERAL_TO_ADD, + Storage.ActionType.Mint + ); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, value, dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + { + bytes memory data = abi.encodeWithSelector( + ISettersGuardian.togglePause.selector, + COLLATERAL_TO_ADD, + Storage.ActionType.Burn + ); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, value, dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + { + bytes memory data = abi.encodeWithSelector( + ISettersGuardian.setStablecoinCap.selector, + COLLATERAL_TO_ADD, + capStablecoin + ); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, value, dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + } + + bytes memory payloadMultiSend = abi.encodeWithSelector(MultiSend.multiSend.selector, transactions); + address multiSend = address(_chainToMultiSend(chainId)); + _serializeJson( + chainId, + multiSend, + 0, + payloadMultiSend, + Enum.Operation.DelegateCall, + hex"", + _chainToContract(chainId, ContractType.GovernorMultisig) + ); + } +} diff --git a/scripts/foundry/transmuter/TransmuterAddCollateralXEVT.s..sol b/scripts/foundry/transmuter/TransmuterAddCollateralXEVT.s..sol new file mode 100644 index 0000000..a796ea6 --- /dev/null +++ b/scripts/foundry/transmuter/TransmuterAddCollateralXEVT.s..sol @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.19; + +import { console } from "forge-std/console.sol"; +import "transmuter/transmuter/Storage.sol" as Storage; +import { ITransmuter, ISettersGovernor, ISettersGuardian, ISwapper } from "transmuter/interfaces/ITransmuter.sol"; +import { Enum } from "safe/Safe.sol"; +import { MultiSend, Utils } from "../Utils.s.sol"; +import "../Constants.s.sol"; + +contract TransmuterAddCollateralXEVT is Utils { + address public constant COLLATERAL_TO_ADD = 0x3Ee320c9F73a84D1717557af00695A34b26d1F1d; + + bytes oracleConfigCollatToAdd; + uint64[] public xFeeMint; + int64[] public yFeeMint; + uint64[] public xFeeBurn; + int64[] public yFeeBurn; + address public agToken; + + function run() external { + uint256 chainId = vm.envUint("CHAIN_ID"); + + ITransmuter transmuter = ITransmuter(_chainToContract(chainId, ContractType.TransmuterAgEUR)); + agToken = address(transmuter.agToken()); + bytes memory transactions; + uint8 isDelegateCall = 0; + address to = address(transmuter); + uint256 value = 0; + + xFeeBurn = new uint64[](3); + xFeeMint = new uint64[](3); + yFeeMint = new int64[](xFeeMint.length); + yFeeBurn = new int64[](xFeeBurn.length); + xFeeBurn[0] = 1e9; + xFeeBurn[1] = 0.06e9; + xFeeBurn[2] = 0.05e9; + yFeeBurn[0] = 0.005e9; + yFeeBurn[1] = 0.005e9; + yFeeBurn[2] = 0.999e9; + + xFeeMint[0] = 0; + xFeeMint[1] = 0.19e9; + xFeeMint[2] = 0.20e9; + yFeeMint[0] = 0; + yFeeMint[1] = 0; + yFeeMint[2] = 100e9 - 1; + + // Add the new collateral + { + { + address oracle = 0x6B102047A4bB943DE39233E44487F2d57bDCb33e; + uint256 normalizationFactor = 1e18; // price == 36 decimals + bytes memory readData; + bytes memory targetData = abi.encode(oracle, normalizationFactor); + oracleConfigCollatToAdd = abi.encode( + Storage.OracleReadType.NO_ORACLE, + Storage.OracleReadType.MORPHO_ORACLE, + readData, + targetData, + abi.encode(uint128(0), uint128(0)) + ); + } + { + bytes memory data = abi.encodeWithSelector(ISettersGovernor.addCollateral.selector, COLLATERAL_TO_ADD); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, value, dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + { + // Mint fees + bytes memory data = abi.encodeWithSelector( + ISettersGuardian.setFees.selector, + COLLATERAL_TO_ADD, + xFeeMint, + yFeeMint, + true + ); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, value, dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + { + // Burn fees + bytes memory data = abi.encodeWithSelector( + ISettersGuardian.setFees.selector, + COLLATERAL_TO_ADD, + xFeeBurn, + yFeeBurn, + false + ); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, value, dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + { + bytes memory data = abi.encodeWithSelector( + ISettersGovernor.setOracle.selector, + COLLATERAL_TO_ADD, + oracleConfigCollatToAdd + ); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, value, dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + { + bytes memory data = abi.encodeWithSelector( + ISettersGuardian.togglePause.selector, + COLLATERAL_TO_ADD, + Storage.ActionType.Mint + ); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, value, dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + { + bytes memory data = abi.encodeWithSelector( + ISettersGuardian.togglePause.selector, + COLLATERAL_TO_ADD, + Storage.ActionType.Burn + ); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, value, dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + } + + bytes memory payloadMultiSend = abi.encodeWithSelector(MultiSend.multiSend.selector, transactions); + address multiSend = address(_chainToMultiSend(chainId)); + _serializeJson(chainId, multiSend, 0, payloadMultiSend, Enum.Operation.DelegateCall, hex"", _chainToContract(chainId, ContractType.GovernorMultisig)); + } +} diff --git a/scripts/foundry/transmuter/TransmuterRecoverERC20.s.sol b/scripts/foundry/transmuter/TransmuterRecoverERC20.s.sol new file mode 100644 index 0000000..9513f37 --- /dev/null +++ b/scripts/foundry/transmuter/TransmuterRecoverERC20.s.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.19; + +import { console } from "forge-std/console.sol"; +import "transmuter/transmuter/Storage.sol" as Storage; +import { ITransmuter, ISettersGovernor, ISettersGuardian, ISwapper } from "transmuter/interfaces/ITransmuter.sol"; +import { Enum } from "safe/Safe.sol"; +import { IERC20 } from "oz/token/ERC20/extensions/IERC20Metadata.sol"; +import { MultiSend, Utils } from "../Utils.s.sol"; +import "../Constants.s.sol"; + +contract TransmuterRecoverERC20 is Utils { + address[] public erc20ToRecover; + uint256[] public amountToRecover; + + function run() external { + uint256 chainId = vm.envUint("CHAIN_ID"); + + address safe = _chainToContract(chainId, ContractType.GovernorMultisig); + bytes memory transactions; + + erc20ToRecover = new address[](3); + erc20ToRecover[0] = 0xCA30c93B02514f86d5C86a6e375E3A330B435Fb5; + erc20ToRecover[1] = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0; + erc20ToRecover[2] = 0x9994E35Db50125E0DF82e4c2dde62496CE330999; + + amountToRecover = new uint256[](3); + amountToRecover[0] = 319821197533155415531; + amountToRecover[1] = 2795614800816400737; + amountToRecover[2] = 309033239278443116794779; + + { + ITransmuter transmuter = ITransmuter(_chainToContract(chainId, ContractType.TransmuterAgUSD)); + uint8 isDelegateCall = 0; + for (uint256 i = 0; i < erc20ToRecover.length; i++) { + console.log(erc20ToRecover[i]); + address to = address(transmuter); + bytes memory data = abi.encodeWithSelector( + ISettersGovernor.recoverERC20.selector, + address(0), + erc20ToRecover[i], + 0xA9DdD91249DFdd450E81E1c56Ab60E1A62651701, + amountToRecover[i] + ); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, uint256(0), dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + } + + bytes memory payloadMultiSend = abi.encodeWithSelector(MultiSend.multiSend.selector, transactions); + _serializeJson( + chainId, + address(_chainToMultiSend(chainId)), + uint256(0), + payloadMultiSend, + Enum.Operation.DelegateCall, + hex"", + safe + ); + } +} diff --git a/scripts/foundry/transmuter/TransmuterSetFees.s.sol b/scripts/foundry/transmuter/TransmuterSetFees.s.sol new file mode 100644 index 0000000..cbc1809 --- /dev/null +++ b/scripts/foundry/transmuter/TransmuterSetFees.s.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.19; + +import { console } from "forge-std/console.sol"; +import "transmuter/transmuter/Storage.sol" as Storage; +import { ITransmuter, ISettersGovernor, ISettersGuardian, ISwapper } from "transmuter/interfaces/ITransmuter.sol"; +import { Enum } from "safe/Safe.sol"; +import { MultiSend, Utils } from "../Utils.s.sol"; +import { BaseHarvester } from "transmuter/helpers/BaseHarvester.sol"; +import "../Constants.s.sol"; + +contract TransmuterSetFees is Utils { + uint64[] public xFeeBurn; + int64[] public yFeeBurn; + + function run() external { + uint256 chainId = vm.envUint("CHAIN_ID"); + + address safe = _chainToContract(chainId, ContractType.GuardianMultisig); + bytes memory transactions; + + xFeeBurn = new uint64[](1); + yFeeBurn = new int64[](xFeeBurn.length); + xFeeBurn[0] = 1e9; + yFeeBurn[0] = 0; + + { + ITransmuter transmuter = ITransmuter(_chainToContract(chainId, ContractType.TransmuterAgEUR)); + uint8 isDelegateCall = 0; + { + address to = address(transmuter); + bytes memory data = abi.encodeWithSelector(ISettersGuardian.setFees.selector, 0x3Ee320c9F73a84D1717557af00695A34b26d1F1d, xFeeBurn, yFeeBurn, false); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, uint256(0), dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + { + address to = 0x0A10f87F55d89eb2a89c264ebE46C90785a10B77; + bytes memory data = abi.encodeWithSelector(BaseHarvester.setTargetExposure.selector, 0x3Ee320c9F73a84D1717557af00695A34b26d1F1d, 0); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, uint256(0), dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + } + + bytes memory payloadMultiSend = abi.encodeWithSelector(MultiSend.multiSend.selector, transactions); + _serializeJson(chainId, address(_chainToMultiSend(chainId)), uint256(0), payloadMultiSend, Enum.Operation.DelegateCall, hex"", safe); + } +} diff --git a/scripts/foundry/transmuter/TransmuterWhitelistHarvesters.s.sol b/scripts/foundry/transmuter/TransmuterWhitelistHarvesters.s.sol new file mode 100644 index 0000000..3939635 --- /dev/null +++ b/scripts/foundry/transmuter/TransmuterWhitelistHarvesters.s.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.19; + +import { console } from "forge-std/console.sol"; +import "transmuter/transmuter/Storage.sol" as Storage; +import { TrustedType } from "transmuter/transmuter/Storage.sol"; +import { ITransmuter, ISettersGovernor, ISettersGuardian, ISwapper} from "transmuter/interfaces/ITransmuter.sol"; +import { Enum } from "safe/Safe.sol"; +import { MultiSend, Utils } from "../Utils.s.sol"; +import "../Constants.s.sol"; + +contract TransmuterWhitelistHarvesters is Utils { + function run() external { + uint256 chainId = vm.envUint("CHAIN_ID"); + + address safe = _chainToContract(chainId, ContractType.GovernorMultisig); + bytes memory transactions; + + { + ITransmuter transmuter = ITransmuter(_chainToContract(chainId, ContractType.TransmuterAgEUR)); + address to = address(transmuter); + uint8 isDelegateCall = 0; + { + bytes memory data = abi.encodeWithSelector(ISettersGovernor.toggleTrusted.selector, 0x0A10f87F55d89eb2a89c264ebE46C90785a10B77, TrustedType.Seller); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, uint256(0), dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + } + { + ITransmuter transmuter = ITransmuter(_chainToContract(chainId, ContractType.TransmuterAgUSD)); + address to = address(transmuter); + uint8 isDelegateCall = 0; + { + bytes memory data = abi.encodeWithSelector(ISettersGovernor.toggleTrusted.selector, 0x54b96Fee8208Ea7aCe3d415e5c14798112909794, TrustedType.Seller); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, uint256(0), dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + { + bytes memory data = abi.encodeWithSelector(ISettersGovernor.toggleTrusted.selector, 0x5BEdD878CBfaF4dc53EC272A291A6a4C2259369D, TrustedType.Seller); + uint256 dataLength = data.length; + bytes memory internalTx = abi.encodePacked(isDelegateCall, to, uint256(0), dataLength, data); + transactions = abi.encodePacked(transactions, internalTx); + } + } + + bytes memory payloadMultiSend = abi.encodeWithSelector(MultiSend.multiSend.selector, transactions); + _serializeJson(chainId, address(_chainToMultiSend(chainId)), uint256(0), payloadMultiSend, Enum.Operation.DelegateCall, hex"", safe); + } +} diff --git a/scripts/submitFoundryTx.ts b/scripts/submitFoundryTx.ts index 8231acd..2ba9cef 100644 --- a/scripts/submitFoundryTx.ts +++ b/scripts/submitFoundryTx.ts @@ -1,13 +1,13 @@ import { submit } from './utils/submitTx'; import transactionJson from '../scripts/foundry/transaction.json'; -import { registry } from '@angleprotocol/sdk'; async function main() { const chainId = transactionJson['chainId']; delete transactionJson['additionalData']; console.log(transactionJson); - // TODO need to change the destination safe - const safeAddress = transactionJson['safe'] ?? registry(chainId).Governor; + const safeAddress = transactionJson['safe']; + if(!safeAddress) throw new Error('Safe address not found'); + await submit(transactionJson, 0, chainId, safeAddress); } diff --git a/test/transmuter/TransmuterAddCollateralEURCV.t.sol b/test/transmuter/TransmuterAddCollateralEURCV.t.sol new file mode 100644 index 0000000..d7ea078 --- /dev/null +++ b/test/transmuter/TransmuterAddCollateralEURCV.t.sol @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.19; + +import { stdJson } from "forge-std/StdJson.sol"; +import { console } from "forge-std/console.sol"; +import { MockSafe } from "../mock/MockSafe.sol"; +import { BaseTest } from "../BaseTest.t.sol"; +import "../../scripts/foundry/Constants.s.sol"; +import "transmuter/transmuter/Storage.sol" as Storage; +import "transmuter/utils/Errors.sol" as Errors; +import { ITransmuter, ISettersGovernor, ISettersGuardian, ISwapper, IGetters } from "transmuter/interfaces/ITransmuter.sol"; +import { MAX_MINT_FEE, MAX_BURN_FEE } from "transmuter/utils/Constants.sol"; +import { IERC20 } from "oz/token/ERC20/IERC20.sol"; + +contract TransmuterAddCollateralEURCVTest is BaseTest { + using stdJson for string; + + ITransmuter public transmuter; + IAgToken public agToken; + address[] public collateralList; + + function setUp() public override { + super.setUp(); + } + + function testScript() external { + uint256 chainId = json.readUint("$.chainId"); + vm.selectFork(forkIdentifier[chainId]); + + // TODO + StablecoinType fiat = StablecoinType.EUR; + // TODO END + + address gnosisSafe = _chainToContract(chainId, ContractType.GovernorMultisig); + transmuter = ITransmuter(address(_getTransmuter(chainId, fiat))); + agToken = _getAgToken(chainId, fiat); + + address to = json.readAddress("$.to"); + // uint256 value = json.readUint("$.value"); + uint256 operation = json.readUint("$.operation"); + bytes memory payload = json.readBytes("$.data"); + + // Verify that the call will succeed + MockSafe mockSafe = new MockSafe(); + vm.etch(gnosisSafe, address(mockSafe).code); + vm.prank(gnosisSafe); + (bool success, ) = gnosisSafe.call(abi.encode(address(to), payload, operation, 1e7)); + if (!success) revert(); + + collateralList = transmuter.getCollateralList(); + assertEq(agToken.isMinter(address(transmuter)), true); + assertEq(collateralList.length, 4); + assertEq(collateralList[3], address(0x5F7827FDeb7c20b443265Fc2F40845B715385Ff2)); + + // Check parameters are correct for the new collateral + uint256 newCollateralIndex = 3; + address newCollateral = collateralList[newCollateralIndex]; + { + ( + Storage.OracleReadType oracleType, + Storage.OracleReadType targetType, + bytes memory oracleData, + bytes memory targetData, + bytes memory hyperparameters + ) = transmuter.getOracle(newCollateral); + assertEq(uint256(oracleType), uint256(Storage.OracleReadType.NO_ORACLE)); + assertEq(uint256(targetType), uint256(Storage.OracleReadType.STABLE)); + assertEq(oracleData.length, 0); + assertEq(targetData.length, 0); + assertEq(hyperparameters, abi.encode(uint128(0), uint128(0))); + } + + { + (uint64[] memory xFeeMint, int64[] memory yFeeMint) = transmuter.getCollateralMintFees(newCollateral); + (uint64[] memory xFeeBurn, int64[] memory yFeeBurn) = transmuter.getCollateralBurnFees(newCollateral); + + assertEq(xFeeMint.length, 3); + assertEq(yFeeMint.length, 3); + assertEq(xFeeBurn.length, 1); + assertEq(yFeeBurn.length, 1); + + assertEq(xFeeMint[0], 0); + assertEq(xFeeMint[1], 0.29e9); + assertEq(xFeeMint[2], 0.30e9); + assertEq(xFeeBurn[0], 1e9); + + assertEq(yFeeBurn[0], 0.0005e9); + assertEq(yFeeMint[0], 0); + assertEq(yFeeMint[1], 0); + assertEq(yFeeMint[2], 100e9 - 1); + } + + // Check storage new collat + { + Storage.Collateral memory collatInfo = transmuter.getCollateralInfo(newCollateral); + assertEq(collatInfo.isManaged, 0); + assertEq(collatInfo.isMintLive, 1); + assertEq(collatInfo.isBurnLive, 1); + assertEq(collatInfo.decimals, 18); + assertEq(collatInfo.onlyWhitelisted, 0); + assertEq(collatInfo.normalizedStables, 0); + assertEq(collatInfo.managerData.subCollaterals.length, 0); + assertEq(collatInfo.managerData.config.length, 0); + } + + // Test oracle values returned + { + (uint256 mint, uint256 burn, uint256 ratio, uint256 minRatio, uint256 redemption) = transmuter + .getOracleValues(newCollateral); + assertEq(mint, BASE_18); + assertEq(burn, BASE_18); + assertEq(ratio, BASE_18); + assertEq(redemption, BASE_18); + } + + // Check quotes are working on the added collateral + { + // we ca do some quoteIn and quoteOut + assertEq(BASE_18, transmuter.quoteOut(BASE_18, newCollateral, address(agToken))); + assertEq(BASE_18, transmuter.quoteIn(BASE_18, newCollateral, address(agToken))); + } + + transmuter.quoteRedemptionCurve(BASE_18); + } +} diff --git a/test/transmuter/TransmuterAddCollateralMWEURC.t.sol b/test/transmuter/TransmuterAddCollateralMWEURC.t.sol new file mode 100644 index 0000000..8a5f4ba --- /dev/null +++ b/test/transmuter/TransmuterAddCollateralMWEURC.t.sol @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.19; + +import { stdJson } from "forge-std/StdJson.sol"; +import { console } from "forge-std/console.sol"; +import { MockSafe } from "../mock/MockSafe.sol"; +import { BaseTest } from "../BaseTest.t.sol"; +import "../../scripts/foundry/Constants.s.sol"; +import "transmuter/transmuter/Storage.sol" as Storage; +import "transmuter/utils/Errors.sol" as Errors; +import { ITransmuter, ISettersGovernor, ISettersGuardian, ISwapper, IGetters } from "transmuter/interfaces/ITransmuter.sol"; +import { MAX_MINT_FEE, MAX_BURN_FEE } from "transmuter/utils/Constants.sol"; +import { IERC20 } from "oz/token/ERC20/IERC20.sol"; +import { IERC4626 } from "oz/token/ERC20/extensions/ERC4626.sol"; + +contract TransmuterAddCollateralMWEURCTest is BaseTest { + using stdJson for string; + + ITransmuter public transmuter; + IAgToken public agToken; + address[] public collateralList; + + function setUp() public override { + super.setUp(); + } + + function testScript() external { + uint256 chainId = json.readUint("$.chainId"); + vm.selectFork(forkIdentifier[chainId]); + + // TODO + StablecoinType fiat = StablecoinType.EUR; + // TODO END + + address gnosisSafe = _chainToContract(chainId, ContractType.GovernorMultisig); + transmuter = ITransmuter(address(_getTransmuter(chainId, fiat))); + agToken = _getAgToken(chainId, fiat); + + address to = json.readAddress("$.to"); + // uint256 value = json.readUint("$.value"); + uint256 operation = json.readUint("$.operation"); + bytes memory payload = json.readBytes("$.data"); + + // Verify that the call will succeed + MockSafe mockSafe = new MockSafe(); + vm.etch(gnosisSafe, address(mockSafe).code); + vm.prank(gnosisSafe); + (bool success, ) = gnosisSafe.call(abi.encode(address(to), payload, operation, 1e7)); + if (!success) revert(); + + uint256 newCollateralIndex = 1; + collateralList = transmuter.getCollateralList(); + assertEq(agToken.isMinter(address(transmuter)), true); + assertEq(collateralList.length, 2); + assertEq(collateralList[newCollateralIndex], address(0xf24608E0CCb972b0b0f4A6446a0BBf58c701a026)); + + // Check parameters are correct for the new collateral + address newCollateral = collateralList[newCollateralIndex]; + { + ( + Storage.OracleReadType oracleType, + Storage.OracleReadType targetType, + bytes memory oracleData, + bytes memory targetData, + bytes memory hyperparameters + ) = transmuter.getOracle(newCollateral); + + bytes memory readData; + { + address oracle = 0xa7ea0d40C246b876F76713Ba9a9A95f3f18AB794; + uint256 normalizationFactor = 1e18; + readData = abi.encode(oracle, normalizationFactor); + } + assertEq(uint256(oracleType), uint256(Storage.OracleReadType.MORPHO_ORACLE)); + assertEq(uint256(targetType), uint256(Storage.OracleReadType.MAX)); + assertEq(oracleData, readData); + assertEq(targetData, abi.encode(1000794000000000000)); + assertEq(hyperparameters, abi.encode(uint128(0), uint128(0.0005e18))); + } + + { + (uint64[] memory xFeeMint, int64[] memory yFeeMint) = transmuter.getCollateralMintFees(newCollateral); + (uint64[] memory xFeeBurn, int64[] memory yFeeBurn) = transmuter.getCollateralBurnFees(newCollateral); + + assertEq(xFeeMint.length, 3); + assertEq(yFeeMint.length, 3); + assertEq(xFeeBurn.length, 3); + assertEq(yFeeBurn.length, 3); + + assertEq(xFeeBurn[0], 1e9); + assertEq(xFeeBurn[1], 0.21e9); + assertEq(xFeeBurn[2], 0.20e9); + assertEq(yFeeBurn[0], 0.005e9); + assertEq(yFeeBurn[1], 0.005e9); + assertEq(yFeeBurn[2], int64(uint64(MAX_BURN_FEE))); + + assertEq(xFeeMint[0], 0); + assertEq(xFeeMint[1], 0.59e9); + assertEq(xFeeMint[2], 0.60e9); + assertEq(yFeeMint[0], 0.0005e9); + assertEq(yFeeMint[1], 0.0005e9); + assertEq(yFeeMint[2], int64(uint64(MAX_MINT_FEE))); + } + + // Check storage new collat + { + Storage.Collateral memory collatInfo = transmuter.getCollateralInfo(newCollateral); + assertEq(collatInfo.isManaged, 0); + assertEq(collatInfo.isMintLive, 1); + assertEq(collatInfo.isBurnLive, 1); + assertEq(collatInfo.decimals, 18); + assertEq(collatInfo.onlyWhitelisted, 0); + assertEq(collatInfo.normalizedStables, 0); + assertEq(collatInfo.managerData.subCollaterals.length, 0); + assertEq(collatInfo.managerData.config.length, 0); + } + + { + // Test oracle values returned + uint256 value = IERC4626(newCollateral).convertToAssets(1 ether) * 1e12; + (uint256 mint, uint256 burn, uint256 ratio, uint256 minRatio, uint256 redemption) = transmuter + .getOracleValues(newCollateral); + assertApproxEqAbs(mint, value, 0.01 ether); + assertApproxEqAbs(burn, value, 0.01 ether); + assertApproxEqAbs(ratio, BASE_18, 0.01 ether); + assertApproxEqAbs(redemption, value, 0.01 ether); + } + + // Check quotes are working on the added collateral + { + // we can do some quoteIn and quoteOut + uint256 mintedAmount = transmuter.quoteIn(1 ether, newCollateral, address(agToken)); + assertApproxEqAbs(mintedAmount, 1 ether, 0.01 ether); + uint256 fromAmount = transmuter.quoteOut(1 ether, newCollateral, address(agToken)); + assertApproxEqAbs(fromAmount, 1 ether, 0.01 ether); + } + + transmuter.quoteRedemptionCurve(BASE_18); + } +} diff --git a/test/transmuter/TransmuterAddCollateralXEVT.t.sol b/test/transmuter/TransmuterAddCollateralXEVT.t.sol new file mode 100644 index 0000000..8d16df5 --- /dev/null +++ b/test/transmuter/TransmuterAddCollateralXEVT.t.sol @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.19; + +import { stdJson } from "forge-std/StdJson.sol"; +import { console } from "forge-std/console.sol"; +import { MockSafe } from "../mock/MockSafe.sol"; +import { BaseTest } from "../BaseTest.t.sol"; +import "../../scripts/foundry/Constants.s.sol"; +import "transmuter/transmuter/Storage.sol" as Storage; +import "transmuter/utils/Errors.sol" as Errors; +import { ITransmuter, ISettersGovernor, ISettersGuardian, ISwapper, IGetters } from "transmuter/interfaces/ITransmuter.sol"; +import { MAX_MINT_FEE, MAX_BURN_FEE } from "transmuter/utils/Constants.sol"; +import { IERC20 } from "oz/token/ERC20/IERC20.sol"; +import { IERC4626 } from "oz/token/ERC20/extensions/ERC4626.sol"; + +contract TransmuterAddCollateralXEVTTest is BaseTest { + using stdJson for string; + + ITransmuter public transmuter; + IAgToken public agToken; + address[] public collateralList; + + function setUp() public override { + super.setUp(); + } + + function testScript() external { + uint256 chainId = json.readUint("$.chainId"); + vm.selectFork(forkIdentifier[chainId]); + + // TODO + StablecoinType fiat = StablecoinType.EUR; + // TODO END + + address gnosisSafe = _chainToContract(chainId, ContractType.GovernorMultisig); + transmuter = ITransmuter(address(_getTransmuter(chainId, fiat))); + agToken = _getAgToken(chainId, fiat); + + address to = json.readAddress("$.to"); + // uint256 value = json.readUint("$.value"); + uint256 operation = json.readUint("$.operation"); + bytes memory payload = json.readBytes("$.data"); + + // Verify that the call will succeed + MockSafe mockSafe = new MockSafe(); + vm.etch(gnosisSafe, address(mockSafe).code); + vm.prank(gnosisSafe); + (bool success, ) = gnosisSafe.call(abi.encode(address(to), payload, operation, 1e7)); + if (!success) revert(); + + collateralList = transmuter.getCollateralList(); + assertEq(agToken.isMinter(address(transmuter)), true); + assertEq(collateralList.length, 4); + assertEq(collateralList[3], address(0x3Ee320c9F73a84D1717557af00695A34b26d1F1d)); + + // Check parameters are correct for the new collateral + uint256 newCollateralIndex = 3; + address newCollateral = collateralList[newCollateralIndex]; + { + ( + Storage.OracleReadType oracleType, + Storage.OracleReadType targetType, + bytes memory oracleData, + bytes memory targetData, + bytes memory hyperparameters + ) = transmuter.getOracle(newCollateral); + assertEq(uint256(oracleType), uint256(Storage.OracleReadType.NO_ORACLE)); + assertEq(uint256(targetType), uint256(Storage.OracleReadType.MORPHO_ORACLE)); + assertEq(oracleData.length, 0); + assertNotEq(targetData.length, 0); + assertEq(hyperparameters, abi.encode(uint128(0), uint128(0))); + } + + { + (uint64[] memory xFeeMint, int64[] memory yFeeMint) = transmuter.getCollateralMintFees(newCollateral); + (uint64[] memory xFeeBurn, int64[] memory yFeeBurn) = transmuter.getCollateralBurnFees(newCollateral); + + assertEq(xFeeMint.length, 3); + assertEq(yFeeMint.length, 3); + assertEq(xFeeBurn.length, 3); + assertEq(yFeeBurn.length, 3); + + assertEq(xFeeMint[0], 0); + assertEq(xFeeMint[1], 0.19e9); + assertEq(xFeeMint[2], 0.20e9); + assertEq(xFeeBurn[0], 1e9); + assertEq(xFeeBurn[1], 0.06e9); + assertEq(xFeeBurn[2], 0.05e9); + + assertEq(yFeeBurn[0], 0.005e9); + assertEq(yFeeBurn[1], 0.005e9); + assertEq(yFeeBurn[2], 0.999e9); + assertEq(yFeeMint[0], 0); + assertEq(yFeeMint[1], 0); + assertEq(yFeeMint[2], 100e9 - 1); + } + + // Check storage new collat + { + Storage.Collateral memory collatInfo = transmuter.getCollateralInfo(newCollateral); + assertEq(collatInfo.isManaged, 0); + assertEq(collatInfo.isMintLive, 1); + assertEq(collatInfo.isBurnLive, 1); + assertEq(collatInfo.decimals, 6); + assertEq(collatInfo.onlyWhitelisted, 0); + assertEq(collatInfo.normalizedStables, 0); + assertEq(collatInfo.managerData.subCollaterals.length, 0); + assertEq(collatInfo.managerData.config.length, 0); + } + + // Test oracle values returned + uint256 value = IERC4626(newCollateral).convertToAssets(1e6) * 1e12; + { + (uint256 mint, uint256 burn, uint256 ratio, uint256 minRatio, uint256 redemption) = transmuter + .getOracleValues(newCollateral); + assertEq(mint, value); + assertEq(burn, value); + assertEq(ratio, BASE_18); + assertEq(redemption, value); + } + + // Check quotes are working on the added collateral + { + // we ca do some quoteIn and quoteOut + transmuter.quoteOut(value, newCollateral, address(agToken)); + transmuter.quoteIn(value, newCollateral, address(agToken)); + } + + transmuter.quoteRedemptionCurve(BASE_18); + } +} diff --git a/test/transmuter/TransmuterERC20Recover.t.sol b/test/transmuter/TransmuterERC20Recover.t.sol new file mode 100644 index 0000000..c6990ee --- /dev/null +++ b/test/transmuter/TransmuterERC20Recover.t.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.19; + +import { stdJson } from "forge-std/StdJson.sol"; +import { console } from "forge-std/console.sol"; +import { MockSafe } from "../mock/MockSafe.sol"; +import { BaseTest } from "../BaseTest.t.sol"; +import "../../scripts/foundry/Constants.s.sol"; +import "transmuter/transmuter/Storage.sol" as Storage; +import "transmuter/utils/Errors.sol" as Errors; +import { ITransmuter, ISettersGovernor, ISettersGuardian, ISwapper, IGetters } from "transmuter/interfaces/ITransmuter.sol"; +import { MAX_MINT_FEE, MAX_BURN_FEE } from "transmuter/utils/Constants.sol"; +import { IERC20 } from "oz/token/ERC20/IERC20.sol"; +import { IERC4626 } from "oz/token/ERC20/extensions/ERC4626.sol"; +import { BaseHarvester } from "transmuter/helpers/BaseHarvester.sol"; + +contract TransmuterRecoverERC20Test is BaseTest { + using stdJson for string; + + ITransmuter public transmuter; + address[] public erc20ToRecover; + uint256[] public amountToRecover; + uint256[] public balancesBefore; + address public receiver = 0xA9DdD91249DFdd450E81E1c56Ab60E1A62651701; + + function testScript() external { + erc20ToRecover = new address[](3); + erc20ToRecover[0] = 0xCA30c93B02514f86d5C86a6e375E3A330B435Fb5; + erc20ToRecover[1] = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0; + erc20ToRecover[2] = 0x9994E35Db50125E0DF82e4c2dde62496CE330999; + + amountToRecover = new uint256[](3); + amountToRecover[0] = 319821197533155415531; + amountToRecover[1] = 2795614800816400737; + amountToRecover[2] = 309033239278443116794779; + + balancesBefore = new uint256[](erc20ToRecover.length); + + uint256 chainId = json.readUint("$.chainId"); + address safe = json.readAddress("$.safe"); + vm.selectFork(forkIdentifier[chainId]); + transmuter = transmuter = ITransmuter(_chainToContract(chainId, ContractType.TransmuterAgEUR)); + + address to = json.readAddress("$.to"); + // uint256 value = json.readUint("$.value"); + uint256 operation = json.readUint("$.operation"); + bytes memory payload = json.readBytes("$.data"); + + for (uint256 i = 0; i < erc20ToRecover.length; i++) { + balancesBefore[i] = IERC20(erc20ToRecover[i]).balanceOf(receiver); + } + + // Verify that the call will succeed + MockSafe mockSafe = new MockSafe(); + vm.etch(safe, address(mockSafe).code); + vm.prank(safe); + (bool success, ) = safe.call(abi.encode(address(to), payload, operation, 1e7)); + if (!success) revert(); + + for (uint256 i = 0; i < erc20ToRecover.length; i++) { + uint256 balanceAfter = IERC20(erc20ToRecover[i]).balanceOf(receiver); + assertEq(balanceAfter, balancesBefore[i] + amountToRecover[i]); + } + } +} diff --git a/test/transmuter/TransmuterSetFees.t.sol b/test/transmuter/TransmuterSetFees.t.sol new file mode 100644 index 0000000..349241b --- /dev/null +++ b/test/transmuter/TransmuterSetFees.t.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.19; + +import { stdJson } from "forge-std/StdJson.sol"; +import { console } from "forge-std/console.sol"; +import { MockSafe } from "../mock/MockSafe.sol"; +import { BaseTest } from "../BaseTest.t.sol"; +import "../../scripts/foundry/Constants.s.sol"; +import "transmuter/transmuter/Storage.sol" as Storage; +import "transmuter/utils/Errors.sol" as Errors; +import { ITransmuter, ISettersGovernor, ISettersGuardian, ISwapper, IGetters } from "transmuter/interfaces/ITransmuter.sol"; +import { MAX_MINT_FEE, MAX_BURN_FEE } from "transmuter/utils/Constants.sol"; +import { IERC20 } from "oz/token/ERC20/IERC20.sol"; +import { IERC4626 } from "oz/token/ERC20/extensions/ERC4626.sol"; +import { BaseHarvester } from "transmuter/helpers/BaseHarvester.sol"; + +contract TransmuterSetFeesTest is BaseTest { + using stdJson for string; + + ITransmuter public transmuter; + IAgToken public agToken; + + function testScript() external { + uint256 chainId = json.readUint("$.chainId"); + address safe = json.readAddress("$.safe"); + vm.selectFork(forkIdentifier[chainId]); + + + transmuter = transmuter = ITransmuter(_chainToContract(chainId, ContractType.TransmuterAgEUR)); + agToken = IAgToken(address(transmuter.agToken())); + + address to = json.readAddress("$.to"); + // uint256 value = json.readUint("$.value"); + uint256 operation = json.readUint("$.operation"); + bytes memory payload = json.readBytes("$.data"); + + // Verify that the call will succeed + MockSafe mockSafe = new MockSafe(); + vm.etch(safe, address(mockSafe).code); + vm.prank(safe); + (bool success, ) = safe.call(abi.encode(address(to), payload, operation, 1e7)); + if (!success) revert(); + + address collateral = 0x3Ee320c9F73a84D1717557af00695A34b26d1F1d; + { + (uint64[] memory xFeeBurn, int64[] memory yFeeBurn) = transmuter.getCollateralBurnFees(collateral); + + assertEq(xFeeBurn.length, 1); + assertEq(yFeeBurn.length, 1); + + assertEq(xFeeBurn[0], 1e9); + assertEq(yFeeBurn[0], 0); + } + + // Check quotes are working on the added collateral + { + // we ca do some quoteIn and quoteOut + transmuter.quoteOut(BASE_18, collateral, address(agToken)); + transmuter.quoteIn(BASE_18, collateral, address(agToken)); + } + + transmuter.quoteRedemptionCurve(BASE_18); + + { + BaseHarvester harvester = BaseHarvester(0x0A10f87F55d89eb2a89c264ebE46C90785a10B77); + (, uint64 target,,,) = harvester.yieldBearingData(collateral); + assertEq(target, 0); + } + } +} diff --git a/test/transmuter/TransmuterWhitelistHarvesters.t.sol b/test/transmuter/TransmuterWhitelistHarvesters.t.sol new file mode 100644 index 0000000..9d5100c --- /dev/null +++ b/test/transmuter/TransmuterWhitelistHarvesters.t.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.19; + +import { stdJson } from "forge-std/StdJson.sol"; +import { console } from "forge-std/console.sol"; +import { MockSafe } from "../mock/MockSafe.sol"; +import { BaseTest } from "../BaseTest.t.sol"; +import "../../scripts/foundry/Constants.s.sol"; +import "transmuter/transmuter/Storage.sol" as Storage; +import "transmuter/utils/Errors.sol" as Errors; +import { ITransmuter, ISettersGovernor, ISettersGuardian, ISwapper, IGetters } from "transmuter/interfaces/ITransmuter.sol"; +import { MAX_MINT_FEE, MAX_BURN_FEE } from "transmuter/utils/Constants.sol"; +import { IERC20 } from "oz/token/ERC20/IERC20.sol"; + +contract TransmuterWhitelistHarvestersTest is BaseTest { + using stdJson for string; + + ITransmuter public transmuter; + + function testScript() external { + uint256 chainId = json.readUint("$.chainId"); + address gnosisSafe = json.readAddress("$.safe"); + vm.selectFork(forkIdentifier[chainId]); + + address to = json.readAddress("$.to"); + // uint256 value = json.readUint("$.value"); + uint256 operation = json.readUint("$.operation"); + bytes memory payload = json.readBytes("$.data"); + + // Verify that the call will succeed + MockSafe mockSafe = new MockSafe(); + vm.etch(gnosisSafe, address(mockSafe).code); + vm.prank(gnosisSafe); + (bool success, ) = gnosisSafe.call(abi.encode(address(to), payload, operation, 1e7)); + if (!success) revert(); + + { + transmuter = ITransmuter(_chainToContract(chainId, ContractType.TransmuterAgEUR)); + assertEq(IGetters(address(transmuter)).isTrustedSeller(address(0x0A10f87F55d89eb2a89c264ebE46C90785a10B77)), true); + } + { + transmuter = ITransmuter(_chainToContract(chainId, ContractType.TransmuterAgUSD)); + assertEq(IGetters(address(transmuter)).isTrustedSeller(address(0x54b96Fee8208Ea7aCe3d415e5c14798112909794)), true); + assertEq(IGetters(address(transmuter)).isTrustedSeller(address(0x5BEdD878CBfaF4dc53EC272A291A6a4C2259369D)), true); + } + } +} diff --git a/utils/forwardUtils.js b/utils/forwardUtils.js index 0eaa8cd..d5679d2 100755 --- a/utils/forwardUtils.js +++ b/utils/forwardUtils.js @@ -8,7 +8,7 @@ if (process.argv.length < 3) { const command = process.argv[2]; const extraArgs = process.argv.slice(3).join(' '); -exec(`node lib/utils/utils/${command}.js ${extraArgs}`, (error, stdout, stderr) => { +exec(`bun run lib/utils/utils/${command}.js ${extraArgs}`, (error, stdout, stderr) => { if (error) { console.log(error); process.exit(1); diff --git a/yarn.lock b/yarn.lock index a2387e5..8008ff8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,21 +2,24 @@ # yarn lockfile v1 -"@angleprotocol/sdk@^v2.28.19": - version "2.28.19" - resolved "https://npm.pkg.github.com/download/@angleprotocol/sdk/2.28.19/2f7a23045c30ff8a246a7d0a7b17bb4d491a8d5e#2f7a23045c30ff8a246a7d0a7b17bb4d491a8d5e" - integrity sha512-2Db7OVy7iVMyKVvdlJBupsHIJJhLwUVNIyvfY7B1z5SrigXeIAM/SBjVzduqKEDsRPhPvlEWASsRlf6zt5+JRQ== +"@angleprotocol/sdk@^v2.33.34": + version "2.33.34" + resolved "https://npm.pkg.github.com/download/@angleprotocol/sdk/2.33.34/0268b1d3f9ac2b37d3a7eb267aa077f24fa36426#0268b1d3f9ac2b37d3a7eb267aa077f24fa36426" + integrity sha512-GPVN8azvCgqT8F/rGp5d0m4uJ8ZFO7wKwfOFgLEK2bM5V0UziQBWX/2sbLKQNUzEcNVklRqnK/nNufFDrPqxwg== dependencies: "@apollo/client" "^3.7.17" "@typechain/ethers-v5" "^10.0.0" - "@types/lodash" "^4.14.180" + bun-types "^1.1.27" + class-transformer "^0.5.1" + class-validator "^0.14.1" + cross-fetch "^4.0.0" ethers "^5.6.4" graphql "^15.7.1" graphql-request "^3.6.1" jsbi "^4.3.0" keccak256 "^1.0.6" - lodash "^4.17.21" merkletreejs "^0.3.10" + reflect-metadata "^0.2.2" tiny-invariant "^1.1.0" typechain "^8.3.2" @@ -1884,11 +1887,6 @@ "@types/level-errors" "*" "@types/node" "*" -"@types/lodash@^4.14.180": - version "4.17.5" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.5.tgz#e6c29b58e66995d57cd170ce3e2a61926d55ee04" - integrity sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw== - "@types/lru-cache@^5.1.0": version "5.1.1" resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.1.tgz#c48c2e27b65d2a153b19bfc1a317e30872e01eef" @@ -1929,6 +1927,13 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.66.tgz#dd035d409df322acc83dff62a602f12a5783bbb3" integrity sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw== +"@types/node@~20.12.8": + version "20.12.14" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.14.tgz#0c5cf7ef26aedfd64b0539bba9380ed1f57dcc77" + integrity sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg== + dependencies: + undici-types "~5.26.4" + "@types/pbkdf2@^3.0.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1" @@ -1953,6 +1958,18 @@ dependencies: "@types/node" "*" +"@types/validator@^13.11.8": + version "13.12.2" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.12.2.tgz#760329e756e18a4aab82fc502b51ebdfebbe49f5" + integrity sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA== + +"@types/ws@~8.5.10": + version "8.5.13" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.13.tgz#6414c280875e2691d0d1e080b05addbf5cb91e20" + integrity sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA== + dependencies: + "@types/node" "*" + "@types/yargs-parser@*": version "20.2.1" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" @@ -2963,6 +2980,14 @@ builtin-status-codes@^3.0.0: resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= +bun-types@^1.1.27: + version "1.1.34" + resolved "https://registry.yarnpkg.com/bun-types/-/bun-types-1.1.34.tgz#cf0e1dc5aa8875573a3acb09bead0f23bab5aca2" + integrity sha512-br5QygTEL/TwB4uQOb96Ky22j4Gq2WxWH/8Oqv20fk5HagwKXo/akB+LiYgSfzexCt6kkcUaVm+bKiPl71xPvw== + dependencies: + "@types/node" "~20.12.8" + "@types/ws" "~8.5.10" + bytes@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" @@ -3278,6 +3303,11 @@ class-is@^1.1.0: resolved "https://registry.yarnpkg.com/class-is/-/class-is-1.1.0.tgz#9d3c0fba0440d211d843cec3dedfa48055005825" integrity sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw== +class-transformer@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336" + integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw== + class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -3288,6 +3318,15 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +class-validator@^0.14.1: + version "0.14.1" + resolved "https://registry.yarnpkg.com/class-validator/-/class-validator-0.14.1.tgz#ff2411ed8134e9d76acfeb14872884448be98110" + integrity sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ== + dependencies: + "@types/validator" "^13.11.8" + libphonenumber-js "^1.10.53" + validator "^13.9.0" + clean-css@4.2.x: version "4.2.4" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178" @@ -3651,6 +3690,13 @@ cross-fetch@^3.0.6: dependencies: node-fetch "^2.6.12" +cross-fetch@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983" + integrity sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g== + dependencies: + node-fetch "^2.6.12" + cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -6867,6 +6913,11 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +libphonenumber-js@^1.10.53: + version "1.11.14" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.11.14.tgz#d753524fd30e6433834a1464baf7efed4a06b593" + integrity sha512-sexvAfwcW1Lqws4zFp8heAtAEXbEDnvkYCEGzvOoMgZR7JhXo/IkE9MkkGACgBed5fWqh3ShBGnJBdDnU9N8EQ== + load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -8577,6 +8628,11 @@ reduce-flatten@^2.0.0: resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== +reflect-metadata@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz#400c845b6cba87a21f2c65c4aeb158f4fa4d9c5b" + integrity sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q== + regenerator-runtime@^0.13.4: version "0.13.9" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" @@ -10120,6 +10176,11 @@ underscore@^1.8.3: resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.2.tgz#276cea1e8b9722a8dbed0100a407dda572125881" integrity sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g== +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -10322,6 +10383,11 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +validator@^13.9.0: + version "13.12.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.12.0.tgz#7d78e76ba85504da3fee4fd1922b385914d4b35f" + integrity sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg== + varint@^5.0.0: version "5.0.2" resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4"