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
117 changes: 117 additions & 0 deletions scripts/foundry/transmuter/TransmuterAddCollateralEURCV.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// 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;
uint256 constant BPS = 1e14;

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 xFeeMint, int64[] memory yFeeMint) = transmuter.getCollateralMintFees(EUROC);
(uint64[] memory xFeeBurn, int64[] memory yFeeBurn) = transmuter.getCollateralBurnFees(EUROC);
0xtekgrinder marked this conversation as resolved.
Show resolved Hide resolved

// Add the new collateral
{
{
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(50 * BPS))
0xtekgrinder marked this conversation as resolved.
Show resolved Hide resolved
);
}
{
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"");
}
}
133 changes: 133 additions & 0 deletions scripts/foundry/transmuter/TransmuterAddCollateralXEVT.s..sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// 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 = 0x3Ee320c9F73a84D1717557af00695A34b26d1F1d;
uint256 constant BPS = 1e14;

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);
xFeeMint[0] = 0;
xFeeMint[1] = 190000000;
0xtekgrinder marked this conversation as resolved.
Show resolved Hide resolved
xFeeMint[2] = 200000000;
xFeeBurn[0] = 1000000000;
xFeeBurn[1] = 60000000;
xFeeBurn[2] = 50000000;

yFeeBurn[0] = 5000000;
yFeeBurn[1] = 5000000;
yFeeBurn[2] = 999000000;
yFeeMint[0] = 0;
yFeeMint[1] = 0;
yFeeMint[2] = 999999999999;

// Add the new collateral
{
{
address oracle;
0xtekgrinder marked this conversation as resolved.
Show resolved Hide resolved
uint256 normalizationFactor;
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(50 * BPS))
0xtekgrinder marked this conversation as resolved.
Show resolved Hide resolved
);
}
{
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"");
0xtekgrinder marked this conversation as resolved.
Show resolved Hide resolved
}
}
129 changes: 129 additions & 0 deletions test/transmuter/TransmuterAddCollateralEURCV.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// 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;

uint256 constant BPS = 1e14;

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(50 * BPS)));
}

{
(uint64[] memory xFeeMintExpected, int64[] memory yFeeMintExpected) = transmuter.getCollateralMintFees(EUROC);
(uint64[] memory xFeeBurnExpected, int64[] memory yFeeBurnExpected) = transmuter.getCollateralBurnFees(EUROC);

(uint64[] memory xFeeMint, int64[] memory yFeeMint) = transmuter.getCollateralMintFees(newCollateral);
(uint64[] memory xFeeBurn, int64[] memory yFeeBurn) = transmuter.getCollateralBurnFees(newCollateral);

assertEq(xFeeMint.length, xFeeMintExpected.length);
assertEq(yFeeMint.length, yFeeMintExpected.length);
assertEq(xFeeBurn.length, xFeeBurnExpected.length);
assertEq(yFeeBurn.length, yFeeBurnExpected.length);

for (uint256 i = 0; i < xFeeMint.length; i++) {
assertEq(xFeeMint[i], xFeeMintExpected[i]);
assertEq(yFeeMint[i], yFeeMintExpected[i]);
}
for (uint256 i = 0; i < xFeeBurn.length; i++) {
assertEq(xFeeBurn[i], xFeeBurnExpected[i]);
assertEq(yFeeBurn[i], yFeeBurnExpected[i]);
}
}

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