Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add collaterals #32

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
2 changes: 1 addition & 1 deletion lib/angle-transmuter
Submodule angle-transmuter updated 99 files
+29 −33 .github/workflows/ci-deep.yml
+32 −34 .github/workflows/ci.yml
+1 −0 .gitignore
+0 −4 .gitmodules
+1 −1 .npmrc
+18 −2 .vscode/settings.json
+0 −6 DEBUG.md
+3 −2 README.md
+202 −0 contracts/helpers/BaseHarvester.sol
+113 −0 contracts/helpers/BaseRebalancerFlashloan.sol
+33 −0 contracts/helpers/HarvesterSwap.sol
+50 −0 contracts/helpers/HarvesterVault.sol
+10 −4 contracts/helpers/Rebalancer.sol
+94 −0 contracts/helpers/RebalancerFlashloanSwap.sol
+41 −0 contracts/helpers/RebalancerFlashloanVault.sol
+3 −0 contracts/interfaces/IGetters.sol
+4 −0 contracts/interfaces/IRebalancer.sol
+24 −0 contracts/interfaces/IRebalancerFlashloan.sol
+3 −0 contracts/interfaces/ISetters.sol
+9 −0 contracts/interfaces/external/morpho/IMorphoOracle.sol
+25 −4 contracts/savings/Savings.sol
+35 −0 contracts/savings/nameable/SavingsNameable.sol
+44 −42 contracts/transmuter/Storage.sol
+0 −1 contracts/transmuter/configs/FakeGnosis.sol
+14 −14 contracts/transmuter/configs/Production.sol
+116 −0 contracts/transmuter/configs/ProductionSidechain.sol
+4 −4 contracts/transmuter/configs/ProductionUSD.sol
+5 −0 contracts/transmuter/configs/Test.sol
+0 −1 contracts/transmuter/facets/DiamondEtherscan.sol
+5 −0 contracts/transmuter/facets/Getters.sol
+33 −31 contracts/transmuter/facets/Redeemer.sol
+1 −1 contracts/transmuter/facets/SettersGovernor.sol
+5 −0 contracts/transmuter/facets/SettersGuardian.sol
+17 −4 contracts/transmuter/facets/Swapper.sol
+33 −62 contracts/transmuter/libraries/LibOracle.sol
+9 −0 contracts/transmuter/libraries/LibSetters.sol
+2 −0 contracts/utils/Errors.sol
+13 −18 foundry.toml
+11 −2 helpers/fork.sh
+0 −1 lib/borrow-contracts
+1 −1 lib/forge-std
+1 −1 lib/utils
+8 −7 package.json
+1 −2 remappings.txt
+25 −0 scripts/AdjustYieldExposure.s.sol
+30 −8 scripts/Constants.s.sol
+36 −0 scripts/DeployHarvesterSwap.s.sol
+34 −0 scripts/DeployHarvesterVault.s.sol
+36 −0 scripts/DeployRebalancerFlashloanSwap.s.sol
+39 −0 scripts/DeployRebalancerFlashloanVault.s.sol
+30 −13 scripts/DeploySavings.s.sol
+44 −0 scripts/DeploySavingsImplem.s.sol
+0 −43 scripts/DeploySavingsNoCreate2.s.sol
+13 −9 scripts/DeploySavingsNoImplem.s.sol
+62 −0 scripts/DeployTransmuterSidechain.s.sol
+23 −21 scripts/DeployTransmuterWithoutFacets.s.sol
+90 −36 scripts/Helpers.s.sol
+211 −0 scripts/SetupDeployedTransmuter.s.sol
+36 −0 scripts/UpdateOracle.s.sol
+10 −15 scripts/UpdateTransmuterFacets.s.sol
+14 −2 scripts/generated/DummyDiamondImplementation.sol
+222 −0 scripts/generated/DummyDiamondImplementationSidechain.sol
+1 −1 scripts/gnosis/DeploySavingsGnosis.s.sol
+2 −2 scripts/gnosis/DeployTransmuterGnosis.s.sol
+51 −0 scripts/interaction/transmuter/Swap.s.sol
+4 −4 scripts/selectors.json
+4 −1 scripts/selectors_add.json
+322 −112 scripts/test/UpdateTransmuterFacets.s.sol
+697 −0 scripts/test/UpdateTransmuterFacetsUSDATest.s.sol
+1 −2 scripts/utils/TransmuterDeploymentHelper.s.sol
+6 −5 scripts/utils/VanityAddress.s.sol
+1 −4 scripts/vanity.json
+0 −4 scripts/vanitySavingsAgEUR.json
+0 −4 scripts/vanitySavingsAgUSD.json
+6 −0 scripts/vanitySavingsEUR.json
+6 −0 scripts/vanitySavingsUSD.json
+22 −13 test/fuzz/BurnTest.t.sol
+252 −0 test/fuzz/Harvester.t.sol
+127 −15 test/fuzz/MintTest.t.sol
+112 −171 test/fuzz/OracleTest.t.sol
+89 −0 test/fuzz/RebalancerFlashloanVaultTest.t.sol
+55 −41 test/fuzz/RedeemTest.t.sol
+127 −0 test/fuzz/SavingsNameableTest.t.sol
+67 −4 test/fuzz/SavingsTest.t.sol
+2 −1 test/fuzz/SavingsVestTest.t.sol
+46 −0 test/fuzz/SwapTest.t.sol
+1 −1 test/invariants/actors/TraderWithSplit.t.sol
+21 −0 test/mock/MockMorphoOracle.sol
+13 −0 test/mock/MockRouter.sol
+136 −0 test/scripts/HarvesterSwapUSDATest.t.sol
+237 −0 test/scripts/HarvesterUSDATest.t.sol
+452 −0 test/scripts/RebalancerSwapUSDATest.t.sol
+322 −0 test/scripts/RebalancerUSDATest.t.sol
+1 −0 test/units/Layout.t.sol
+46 −0 test/units/Setters.t.sol
+76 −0 test/units/StablecoinCap.t.sol
+90 −0 test/units/upgrade/SavingsNameableUpgradeTest.t.sol
+2 −2 utils/forwardUtils.js
+6 −6 yarn.lock
126 changes: 126 additions & 0 deletions scripts/foundry/transmuter/TransmuterAddCollateralEURCV.s.sol
Original file line number Diff line number Diff line change
@@ -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;
0xtekgrinder marked this conversation as resolved.
Show resolved Hide resolved
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);
}
}
132 changes: 132 additions & 0 deletions scripts/foundry/transmuter/TransmuterAddCollateralXEVT.s..sol
Original file line number Diff line number Diff line change
@@ -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;

uint64[] memory xFeeBurn = new uint64[](3);
uint64[] memory xFeeMint = new uint64[](3);
int64[] memory yFeeMint = new int64[](xFeeMint.length);
int64[] memory 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));
}
}
125 changes: 125 additions & 0 deletions test/transmuter/TransmuterAddCollateralEURCV.t.sol
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading