diff --git a/cache/solidity-files-cache.json b/cache/solidity-files-cache.json index 8b1257d8..b118cff9 100644 --- a/cache/solidity-files-cache.json +++ b/cache/solidity-files-cache.json @@ -1283,7 +1283,7 @@ } }, "lib/yieldspace-tv/lib/yield-utils-v2/src/token/ERC20.sol": { - "lastModificationDate": 1680017327438, + "lastModificationDate": 1681829815435, "contentHash": "717becb3eba94d8d3607a84ebd6f88c4", "sourceName": "lib/yieldspace-tv/lib/yield-utils-v2/src/token/ERC20.sol", "solcConfig": { @@ -2540,8 +2540,8 @@ } }, "src/Join.sol": { - "lastModificationDate": 1680017320194, - "contentHash": "eca9de66bf065891e2170228e705c3ca", + "lastModificationDate": 1683108428245, + "contentHash": "1c188a25a875f6636d21d388418d9af0", "sourceName": "src/Join.sol", "solcConfig": { "settings": { @@ -2818,8 +2818,8 @@ } }, "src/WitchBase.sol": { - "lastModificationDate": 1680017418526, - "contentHash": "50b74c34922b28ef610b57a0642fbe6a", + "lastModificationDate": 1683106591415, + "contentHash": "9666ba7ef8deb5d953f4bb14e93a1fa8", "sourceName": "src/WitchBase.sol", "solcConfig": { "settings": { @@ -4540,7 +4540,7 @@ } }, "src/mocks/VRCauldronUpgrade.sol": { - "lastModificationDate": 1680017418526, + "lastModificationDate": 1680019416647, "contentHash": "56555f1374fb7d6b272b8ce1ce8bc3c2", "sourceName": "src/mocks/VRCauldronUpgrade.sol", "solcConfig": { @@ -5626,8 +5626,8 @@ } }, "src/oracles/VariableInterestRateOracle.sol": { - "lastModificationDate": 1680019184619, - "contentHash": "02d61069aa24e565643005e93dd29c98", + "lastModificationDate": 1683539351713, + "contentHash": "9e2d1c63c9b70ed6919d6e3f0c556d2e", "sourceName": "src/oracles/VariableInterestRateOracle.sol", "solcConfig": { "settings": { @@ -5689,8 +5689,8 @@ } }, "src/oracles/accumulator/AccumulatorMultiOracle.sol": { - "lastModificationDate": 1680017320199, - "contentHash": "f77f8696a88dbe9674f1fdd8fc88db25", + "lastModificationDate": 1680270763368, + "contentHash": "b427f8e638ec653b9e35aa27be70bdcf", "sourceName": "src/oracles/accumulator/AccumulatorMultiOracle.sol", "solcConfig": { "settings": { @@ -8439,8 +8439,8 @@ } }, "src/test/fyToken/FYToken.t.sol": { - "lastModificationDate": 1680017320204, - "contentHash": "15841854951d4b33ed24331502ce9abe", + "lastModificationDate": 1683112931162, + "contentHash": "98b7956acabb0ade5078e3566736beaa", "sourceName": "src/test/fyToken/FYToken.t.sol", "solcConfig": { "settings": { @@ -8547,8 +8547,8 @@ } }, "src/test/fyToken/FYTokenFlash.t.sol": { - "lastModificationDate": 1680017320204, - "contentHash": "a95e82148d824ae9a71c490bbb187323", + "lastModificationDate": 1683113086168, + "contentHash": "aaaf8c723842b7c36a8494d95629296e", "sourceName": "src/test/fyToken/FYTokenFlash.t.sol", "solcConfig": { "settings": { @@ -10123,8 +10123,8 @@ } }, "src/test/oracles/VariableIROracle.t.sol": { - "lastModificationDate": 1680019150862, - "contentHash": "9fbc8572ccdf38192df7fc483a11381e", + "lastModificationDate": 1683106480968, + "contentHash": "aee07f582923894ab3a38a8921c800a0", "sourceName": "src/test/oracles/VariableIROracle.t.sol", "solcConfig": { "settings": { @@ -10975,8 +10975,8 @@ } }, "src/test/utils/Mocks.sol": { - "lastModificationDate": 1680017418527, - "contentHash": "4e9bc7ea4e1ffb3dde1d719ff489ee7f", + "lastModificationDate": 1680019933649, + "contentHash": "17842a9be2302ddfa2eaa848887f2ed9", "sourceName": "src/test/utils/Mocks.sol", "solcConfig": { "settings": { @@ -11205,8 +11205,8 @@ } }, "src/test/variable/Fixture.sol": { - "lastModificationDate": 1680018328650, - "contentHash": "2cd06b4d376a2fad5d0d27c04399e18e", + "lastModificationDate": 1683111637203, + "contentHash": "6916277e68be55337404b68b71b80049", "sourceName": "src/test/variable/Fixture.sol", "solcConfig": { "settings": { @@ -11328,8 +11328,8 @@ } }, "src/test/variable/FixtureStates.sol": { - "lastModificationDate": 1680017418528, - "contentHash": "7830c7dc9a17ac015ad56574d8e14de4", + "lastModificationDate": 1683539580167, + "contentHash": "eeddd28109caeafeccef1f6443cfbbf4", "sourceName": "src/test/variable/FixtureStates.sol", "solcConfig": { "settings": { @@ -11468,9 +11468,6 @@ "ETHVaultPouredState": { "0.8.15+commit.e14f2714.Darwin.appleclang": "FixtureStates.sol/ETHVaultPouredState.json" }, - "FlashLoanEnabledState": { - "0.8.15+commit.e14f2714.Darwin.appleclang": "FixtureStates.sol/FlashLoanEnabledState.json" - }, "IlkAddedState": { "0.8.15+commit.e14f2714.Darwin.appleclang": "FixtureStates.sol/IlkAddedState.json" }, @@ -11489,7 +11486,7 @@ } }, "src/test/variable/VRCauldron.t.sol": { - "lastModificationDate": 1680017418528, + "lastModificationDate": 1680019416648, "contentHash": "9b053fd52287d6eab41f6ff2a0add81e", "sourceName": "src/test/variable/VRCauldron.t.sol", "solcConfig": { @@ -11652,8 +11649,8 @@ } }, "src/test/variable/VRLadle.t.sol": { - "lastModificationDate": 1680017418528, - "contentHash": "f649027f383c63468485efabfa0355cc", + "lastModificationDate": 1683539499922, + "contentHash": "d0198d19c72d4699169fcd12d7046e8d", "sourceName": "src/test/variable/VRLadle.t.sol", "solcConfig": { "settings": { @@ -11811,8 +11808,8 @@ } }, "src/test/variable/VRWitch.t.sol": { - "lastModificationDate": 1680017418528, - "contentHash": "1b3919a275955602fd2c73cb78646a64", + "lastModificationDate": 1682518550286, + "contentHash": "f82922d7ad008003b6306f112e9a9f02", "sourceName": "src/test/variable/VRWitch.t.sol", "solcConfig": { "settings": { @@ -11920,8 +11917,8 @@ } }, "src/test/variable/VYToken.t.sol": { - "lastModificationDate": 1680017418528, - "contentHash": "87d27e2a00381251de40a1ada49a38d9", + "lastModificationDate": 1683539604146, + "contentHash": "eb75926ec1e9461325fde8c645c91ad3", "sourceName": "src/test/variable/VYToken.t.sol", "solcConfig": { "settings": { @@ -12040,9 +12037,6 @@ ], "versionRequirement": ">=0.8.13", "artifacts": { - "FlashLoanEnabledStateTests": { - "0.8.15+commit.e14f2714.Darwin.appleclang": "VYToken.t.sol/FlashLoanEnabledStateTests.json" - }, "VYTokenTest": { "0.8.15+commit.e14f2714.Darwin.appleclang": "VYToken.t.sol/VYTokenTest.json" } @@ -12098,8 +12092,8 @@ } }, "src/variable/VRCauldron.sol": { - "lastModificationDate": 1680017418528, - "contentHash": "558b4fc3fe2bf946a1cf5c1d51eee75c", + "lastModificationDate": 1683108449060, + "contentHash": "26c3beeef1c8bb92930128ac070be772", "sourceName": "src/variable/VRCauldron.sol", "solcConfig": { "settings": { @@ -12156,8 +12150,8 @@ } }, "src/variable/VRLadle.sol": { - "lastModificationDate": 1680017418529, - "contentHash": "2b5cc35d6b63c100eb9a8f59db0e92ea", + "lastModificationDate": 1683537232975, + "contentHash": "2060ff176eecec785f993b0d5de0a106", "sourceName": "src/variable/VRLadle.sol", "solcConfig": { "settings": { @@ -12266,8 +12260,8 @@ } }, "src/variable/VRWitch.sol": { - "lastModificationDate": 1680017418529, - "contentHash": "b40889598395269618e1d9f451649d89", + "lastModificationDate": 1683106736144, + "contentHash": "7fac3d524aa222aa5f0fa82edb3de57c", "sourceName": "src/variable/VRWitch.sol", "solcConfig": { "settings": { @@ -12337,8 +12331,8 @@ } }, "src/variable/VYToken.sol": { - "lastModificationDate": 1680018387868, - "contentHash": "c86d5709876604e880d30aba0ef32922", + "lastModificationDate": 1683539531540, + "contentHash": "a4566b72d67b2ddcd36eb087f15a773b", "sourceName": "src/variable/VYToken.sol", "solcConfig": { "settings": { @@ -12369,8 +12363,6 @@ } }, "imports": [ - "lib/ERC3156/contracts/interfaces/IERC3156FlashBorrower.sol", - "lib/ERC3156/contracts/interfaces/IERC3156FlashLender.sol", "lib/openzeppelin-contracts/contracts/interfaces/draft-IERC1822.sol", "lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Upgrade.sol", "lib/openzeppelin-contracts/contracts/proxy/beacon/IBeacon.sol", @@ -12399,8 +12391,8 @@ } }, "src/variable/interfaces/IVRCauldron.sol": { - "lastModificationDate": 1680017418529, - "contentHash": "2c20afbfa5d948e1fbf08694195b40e4", + "lastModificationDate": 1680019933649, + "contentHash": "25cf2462af360f370e38748f1cfee15f", "sourceName": "src/variable/interfaces/IVRCauldron.sol", "solcConfig": { "settings": { diff --git a/remappings.txt b/remappings.txt index 9d8cf101..bf32869f 100644 --- a/remappings.txt +++ b/remappings.txt @@ -3,4 +3,4 @@ forge-std/=lib/forge-std/ erc3156/=lib/ERC3156/ dss-interfaces/=lib/dss-interfaces/ -openzeppelin/=lib/openzeppelin-contracts/ +@openzeppelin/=lib/openzeppelin-contracts/ diff --git a/src/.DS_Store b/src/.DS_Store index c52ed820..6bfa03eb 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/Join.sol b/src/Join.sol index f20ba63d..0d4dec90 100644 --- a/src/Join.sol +++ b/src/Join.sol @@ -58,4 +58,11 @@ contract Join is IJoin, AccessControl { require(address(token) != address(asset), "Use exit for asset"); token.safeTransfer(to, token.balanceOf(address(this))); } + + /// @dev Used to skim the amount of asset token which is the difference between the actual balance and the stored balance. + /// @notice Since fund goes to the `to` address, this function should only be called by authorised accounts. + function skim(address to) external virtual auth { + IERC20 token = IERC20(asset); + token.safeTransfer(to, token.balanceOf(address(this)) - storedBalance); + } } diff --git a/src/WitchBase.sol b/src/WitchBase.sol index 3aec42ce..6ee56108 100644 --- a/src/WitchBase.sol +++ b/src/WitchBase.sol @@ -49,7 +49,10 @@ contract WitchBase is AccessControl, IWitchEvents, IWitchErrors { mapping(bytes6 => mapping(bytes6 => DataTypes.Line)) public lines; mapping(bytes6 => mapping(bytes6 => DataTypes.Limits)) public limits; mapping(address => bool) public protected; - + // The gap is here to avoid overwriting storage variables in future implementations by the contracts + // inheriting from this one. + uint256[10] __gap; + constructor(ICauldron cauldron_, ILadle ladle_) { cauldron = cauldron_; ladle = ladle_; diff --git a/src/oracles/VariableInterestRateOracle.sol b/src/oracles/VariableInterestRateOracle.sol index b4e9c8a8..6298c3de 100644 --- a/src/oracles/VariableInterestRateOracle.sol +++ b/src/oracles/VariableInterestRateOracle.sol @@ -1,4 +1,10 @@ // SPDX-License-Identifier: BUSL-1.1 +// Audited as of 15 May 2023. +// Reports: +// https://github.com/yieldprotocol/variable-rate-audit-gogoauditor/issues/1 +// https://github.com/yieldprotocol/variable-rate-audit-parth-15/issues?q=is%3Aissue+is%3Aclosed +// https://github.com/yieldprotocol/variable-rate-audit-obheda12/issues +// https://github.com/yieldprotocol/variable-rate-audit-DecorativePineapple/issues/19 pragma solidity >=0.8.13; import "@yield-protocol/utils-v2/src/access/AccessControl.sol"; @@ -42,7 +48,7 @@ contract VariableInterestRateOracle is IOracle, AccessControl, Constants { /* Events ******************************************************************************************************************/ - event SourceSet( + event InterestRateParamSet( bytes6 indexed baseId, bytes6 indexed kind, uint256 optimalUsageRate, @@ -51,7 +57,7 @@ contract VariableInterestRateOracle is IOracle, AccessControl, Constants { uint256 slope2, IJoin join ); - event PerSecondRateUpdated( + event InterestRateParamUpdated( bytes6 indexed baseId, bytes6 indexed kind, uint256 optimalUsageRate, @@ -60,16 +66,15 @@ contract VariableInterestRateOracle is IOracle, AccessControl, Constants { uint256 slope2, IJoin join ); + event AccumulatorUpdated(bytes6 indexed baseId, bytes6 indexed kind, uint256 accumulated, uint256 lastUpdateTimestamp, uint256 utilizationRate); constructor(IVRCauldron cauldron_, ILadle ladle_) { cauldron = cauldron_; ladle = ladle_; } - /** - @notice Set a source - */ - function setSource( + /// @notice Set parameters for the given base & kind + function setInterestRateParameters( bytes6 baseId, bytes6 kindId, uint256 optimalUsageRate, @@ -94,7 +99,7 @@ contract VariableInterestRateOracle is IOracle, AccessControl, Constants { ilks: ilks }); - emit SourceSet( + emit InterestRateParamSet( baseId, kindId, optimalUsageRate, @@ -105,6 +110,7 @@ contract VariableInterestRateOracle is IOracle, AccessControl, Constants { ); } + /// @dev Update the parameters for the given base & kind function updateParameters( bytes6 baseId, bytes6 kindId, @@ -127,7 +133,7 @@ contract VariableInterestRateOracle is IOracle, AccessControl, Constants { sources[baseId][kindId].slope2 = slope2; sources[baseId][kindId].join = join; - emit PerSecondRateUpdated( + emit InterestRateParamUpdated( baseId, kindId, optimalUsageRate, @@ -138,6 +144,7 @@ contract VariableInterestRateOracle is IOracle, AccessControl, Constants { ); } + /// @dev Return the accumulated for the given base & kind function peek( bytes32 base, bytes32 kind, @@ -150,14 +157,14 @@ contract VariableInterestRateOracle is IOracle, AccessControl, Constants { returns (uint256 accumulated, uint256 updateTime) { InterestRateParameter memory source = sources[base.b6()][kind.b6()]; - require(source.accumulated != 0, "Source not found"); + require(source.accumulated != 0, "Accumulated rate is zero"); accumulated = source.accumulated; - require(accumulated > 0, "Accumulated rate is zero"); updateTime = source.lastUpdated; } + /// @dev Return the accumulated for the given base & kind function get( bytes32 base, bytes32 kind, @@ -175,54 +182,56 @@ contract VariableInterestRateOracle is IOracle, AccessControl, Constants { uint256 secondsSinceLastUpdate = (block.timestamp - rateParameters.lastUpdated); - if (secondsSinceLastUpdate > 0) { - // Calculate the total debt - uint128 totalDebt; - DataTypes.Debt memory debt_; - debt_ = cauldron.debt(base.b6(), base.b6()); + + // Calculate the total debt + uint128 totalDebt; + DataTypes.Debt memory debt_; + + for (uint256 i = 0; i < rateParameters.ilks.length; i++) { + debt_ = cauldron.debt(base.b6(), rateParameters.ilks[i]); totalDebt = totalDebt + debt_.sum; + } - for (uint256 i = 0; i < rateParameters.ilks.length; i++) { - if (cauldron.ilks(base.b6(), rateParameters.ilks[i])) { - debt_ = cauldron.debt(base.b6(), rateParameters.ilks[i]); - totalDebt = totalDebt + debt_.sum; - } - } - - // Calculate utilization rate - // Total debt / Total Liquidity - uint256 utilizationRate = uint256(totalDebt).wdiv( - rateParameters.join.storedBalance() - ); - - uint256 interestRate; - if (utilizationRate <= rateParameters.optimalUsageRate) { - interestRate = - rateParameters.baseVariableBorrowRate + - utilizationRate.wmul(rateParameters.slope1).wdiv( - rateParameters.optimalUsageRate - ); - } else { - interestRate = - rateParameters.baseVariableBorrowRate + - rateParameters.slope1 + - (utilizationRate - rateParameters.optimalUsageRate) - .wmul(rateParameters.slope2) - .wdiv(1e18 - rateParameters.optimalUsageRate); - } - // Calculate per second rate - interestRate = interestRate / 365 days; - rateParameters.accumulated *= (1e18 + interestRate).wpow( - secondsSinceLastUpdate - ); - rateParameters.accumulated /= 1e18; - rateParameters.lastUpdated = block.timestamp; - - sources[base.b6()][kind.b6()] = rateParameters; + // Calculate utilization rate + // Total debt / Total Liquidity + uint256 utilizationRate = uint256(totalDebt).wdiv( + rateParameters.join.storedBalance() + ); + + uint256 interestRate; + if (utilizationRate <= rateParameters.optimalUsageRate) { + interestRate = + rateParameters.baseVariableBorrowRate + + (utilizationRate * rateParameters.slope1) / + rateParameters.optimalUsageRate; + } else { + interestRate = + rateParameters.baseVariableBorrowRate + + rateParameters.slope1 + + ((utilizationRate - rateParameters.optimalUsageRate) * + rateParameters.slope2) / + (1e18 - rateParameters.optimalUsageRate); } + // Calculate per second rate + interestRate = interestRate / 365 days; + rateParameters.accumulated *= (1e18 + interestRate).wpow( + secondsSinceLastUpdate + ); + rateParameters.accumulated /= 1e18; + rateParameters.lastUpdated = block.timestamp; + + sources[base.b6()][kind.b6()] = rateParameters; accumulated = rateParameters.accumulated; require(accumulated > 0, "Accumulated rate is zero"); updateTime = block.timestamp; + + emit AccumulatorUpdated( + base.b6(), + kind.b6(), + accumulated, + updateTime, + utilizationRate + ); } } diff --git a/src/test/oracles/VariableIROracle.t.sol b/src/test/oracles/VariableIROracle.t.sol index 70b277e5..ba2aea01 100644 --- a/src/test/oracles/VariableIROracle.t.sol +++ b/src/test/oracles/VariableIROracle.t.sol @@ -95,7 +95,7 @@ abstract contract ZeroState is Test, TestConstants { // Setting permissions variableInterestRateOracle.grantRole( - variableInterestRateOracle.setSource.selector, + variableInterestRateOracle.setInterestRateParameters.selector, address(this) ); variableInterestRateOracle.grantRole( @@ -145,7 +145,7 @@ abstract contract ZeroState is Test, TestConstants { underlying = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; variableInterestRateOracle.grantRole( - variableInterestRateOracle.setSource.selector, + variableInterestRateOracle.setInterestRateParameters.selector, address(this) ); variableInterestRateOracle.grantRole( @@ -207,7 +207,7 @@ abstract contract ZeroState is Test, TestConstants { abstract contract WithSourceSet is ZeroState { function setUp() public override { super.setUp(); - variableInterestRateOracle.setSource( + variableInterestRateOracle.setInterestRateParameters( baseOne, RATE, sourceParameters.optimalUsageRate, @@ -221,8 +221,8 @@ abstract contract WithSourceSet is ZeroState { } contract VariableInterestRateOracleOracleTest is ZeroState { - function testSetSourceOnlyOnce() public { - variableInterestRateOracle.setSource( + function testsetInterestRateParametersOnlyOnce() public { + variableInterestRateOracle.setInterestRateParameters( baseOne, RATE, sourceParameters.optimalUsageRate, @@ -233,7 +233,7 @@ contract VariableInterestRateOracleOracleTest is ZeroState { sourceParameters.ilks ); vm.expectRevert("Source is already set"); - variableInterestRateOracle.setSource( + variableInterestRateOracle.setInterestRateParameters( baseOne, RATE, sourceParameters.optimalUsageRate, @@ -258,7 +258,7 @@ contract VariableInterestRateOracleOracleTest is ZeroState { } function testCannotCallStaleInterestRateParameter() public { - variableInterestRateOracle.setSource( + variableInterestRateOracle.setInterestRateParameters( baseOne, RATE, sourceParameters.optimalUsageRate, @@ -281,7 +281,7 @@ contract VariableInterestRateOracleOracleTest is ZeroState { } function testRevertOnSourceUnknown() public { - variableInterestRateOracle.setSource( + variableInterestRateOracle.setInterestRateParameters( baseOne, RATE, sourceParameters.optimalUsageRate, @@ -291,9 +291,9 @@ contract VariableInterestRateOracleOracleTest is ZeroState { sourceParameters.slope2, sourceParameters.ilks ); - vm.expectRevert("Source not found"); + vm.expectRevert("Accumulated rate is zero"); variableInterestRateOracle.peek(bytes32(baseTwo), RATE, WAD); - vm.expectRevert("Source not found"); + vm.expectRevert("Accumulated rate is zero"); variableInterestRateOracle.peek(bytes32(baseOne), CHI, WAD); } } diff --git a/src/test/variable/Fixture.sol b/src/test/variable/Fixture.sol index fe050c77..7de214c8 100644 --- a/src/test/variable/Fixture.sol +++ b/src/test/variable/Fixture.sol @@ -33,7 +33,7 @@ import "@yield-protocol/utils-v2/src/interfaces/IWETH9.sol"; import "@yield-protocol/utils-v2/src/token/IERC20Metadata.sol"; import "@yield-protocol/utils-v2/src/utils/Cast.sol"; import "@yield-protocol/utils-v2/src/utils/Math.sol"; -import "openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; using Cast for uint256; using Cast for uint256; @@ -161,13 +161,6 @@ abstract contract Fixture is Test, TestConstants, TestExtensions { daiJoin = new FlashJoin(address(dai)); baseJoin = new FlashJoin(address(base)); - vyToken = new VYToken( - baseId, - IOracle(address(chiRateOracle)), - IJoin(baseJoin), - base.name(), - base.symbol() - ); /// Orchestrating the protocol setUpOracles(); @@ -181,7 +174,7 @@ abstract contract Fixture is Test, TestConstants, TestExtensions { vyTokenProxy = new ERC1967Proxy( address(vyToken), - abi.encodeWithSignature("initialize(address)", address(this)) + abi.encodeWithSignature("initialize(address,string,string,uint8)", address(this), base.name(), base.symbol(), base.decimals()) ); vyToken = VYToken(address(vyTokenProxy)); diff --git a/src/test/variable/FixtureStates.sol b/src/test/variable/FixtureStates.sol index 934d43db..7b5e50e3 100644 --- a/src/test/variable/FixtureStates.sol +++ b/src/test/variable/FixtureStates.sol @@ -152,27 +152,15 @@ abstract contract ETHVaultPouredAndDebtState is ETHVaultPouredState { abstract contract VYTokenZeroState is ZeroState { address public timelock; - FlashBorrower public borrower; function setUp() public virtual override { super.setUp(); timelock = address(1); vyToken.grantRole(VYToken.mint.selector, address(this)); vyToken.grantRole(VYToken.deposit.selector, address(this)); - vyToken.grantRole(VYToken.setFlashFeeFactor.selector, address(this)); - borrower = new FlashBorrower(vyToken); unit = uint128(10 ** ERC20Mock(address(vyToken)).decimals()); deal(address(vyToken), address(this), unit); deal(address(vyToken.underlying()), address(this), unit); } -} - -abstract contract FlashLoanEnabledState is VYTokenZeroState { - event Transfer(address indexed src, address indexed dst, uint256 wad); - - function setUp() public override { - super.setUp(); - vyToken.setFlashFeeFactor(0); - } -} +} \ No newline at end of file diff --git a/src/test/variable/VRLadle.t.sol b/src/test/variable/VRLadle.t.sol index d4b88668..34dd9651 100644 --- a/src/test/variable/VRLadle.t.sol +++ b/src/test/variable/VRLadle.t.sol @@ -154,18 +154,6 @@ contract VaultTests is VaultBuiltState { ladle.give(vaultId, admin); } - function testOnlyOwnerCouldMove() public { - console.log("cannot move a vault with a different owner"); - vm.prank(admin); - vm.expectRevert("Only origin vault owner"); - ladle.stir(vaultId, otherVaultId, 1, 1); - } - - function testOnlyDestinationVaultOwner() public { - vm.prank(admin); - vm.expectRevert("Only destination vault owner"); - ladle.stir(vaultId, otherVaultId, 0, 1); - } } contract PourTests is VaultBuiltState { @@ -275,129 +263,6 @@ contract PouredStateTests is CauldronPouredState { assertEq(art, ART + FEE); } - function testMoveDebt() public { - console.log("can move debt from one vault to another"); - (bytes12 otherVaultId, ) = ladle.build(baseId, usdcId, 123); - (address owner, , bytes6 ilkId) = cauldron.vaults(vaultId); - deal(cauldron.assets(ilkId), owner, INK); - IERC20(cauldron.assets(ilkId)).approve( - address(ladle.joins(ilkId)), - INK - ); - ladle.pour(otherVaultId, msg.sender, (INK).i128(), 0); - ladle.pour(vaultId, address(this), 0, (ART).i128()); - - (uint128 art, uint128 ink) = cauldron.balances(vaultId); - vm.expectEmit(true, true, true, true); - emit VaultStirred(vaultId, otherVaultId, 0, art); - ladle.stir(vaultId, otherVaultId, 0, art); - - (art, ink) = cauldron.balances(vaultId); - assertEq(ink, INK); - assertEq(art, 0); - - (art, ink) = cauldron.balances(otherVaultId); - assertEq(ink, INK); - assertEq(art, ART); - } - - function testMoveCollateral() public { - console.log("can move collateral from one vault to another"); - (bytes12 otherVaultId, ) = ladle.build(baseId, usdcId, 123); - (uint128 art, uint128 ink) = cauldron.balances(vaultId); - - vm.expectEmit(true, true, true, true); - emit VaultStirred(vaultId, otherVaultId, ink, 0); - ladle.stir(vaultId, otherVaultId, ink, 0); - - (art, ink) = cauldron.balances(vaultId); - assertEq(ink, 0); - assertEq(art, 0); - - (art, ink) = cauldron.balances(otherVaultId); - assertEq(ink, INK); - assertEq(art, 0); - } - - function testMoveDebtAndCollateral() public { - console.log("can move debt and collateral from one vault to another"); - (bytes12 otherVaultId, ) = ladle.build(baseId, usdcId, 123); - ladle.pour(vaultId, address(this), 0, (ART).i128()); - (uint128 art, uint128 ink) = cauldron.balances(vaultId); - - vm.expectEmit(true, true, true, true); - emit VaultStirred(vaultId, otherVaultId, ink, art); - ladle.stir(vaultId, otherVaultId, ink, art); - - (art, ink) = cauldron.balances(vaultId); - assertEq(ink, 0); - assertEq(art, 0); - - (art, ink) = cauldron.balances(otherVaultId); - assertEq(ink, INK); - assertEq(art, ART); - } - - function testMoveCollateralInABatch() public { - console.log("can move collateral from one vault to another in a batch"); - (bytes12 otherVaultId, ) = ladle.build(baseId, usdcId, 123); - (uint128 art, uint128 ink) = cauldron.balances(vaultId); - - bytes[] memory calls = new bytes[](1); - calls[0] = abi.encodeWithSelector( - VRLadle.stir.selector, - vaultId, - otherVaultId, - ink, - 0 - ); - - vm.expectEmit(true, true, true, true); - emit VaultStirred(vaultId, otherVaultId, ink, 0); - ladle.batch(calls); - - (art, ink) = cauldron.balances(vaultId); - assertEq(ink, 0); - assertEq(art, 0); - - (art, ink) = cauldron.balances(otherVaultId); - assertEq(ink, INK); - assertEq(art, 0); - } - - function testMoveDebtInABatch() public { - console.log("can move debt from one vault to another in a batch"); - (bytes12 otherVaultId, ) = ladle.build(baseId, usdcId, 123); - (address owner, , bytes6 ilkId) = cauldron.vaults(vaultId); - deal(cauldron.assets(ilkId), owner, INK); - IERC20(cauldron.assets(ilkId)).approve( - address(ladle.joins(ilkId)), - INK - ); - ladle.pour(otherVaultId, msg.sender, (INK).i128(), 0); - ladle.pour(vaultId, address(this), 0, (ART).i128()); - - bytes[] memory calls = new bytes[](1); - calls[0] = abi.encodeWithSelector( - VRLadle.stir.selector, - vaultId, - otherVaultId, - 0, - ART - ); - - vm.expectEmit(true, true, true, true); - emit VaultStirred(vaultId, otherVaultId, 0, uint128(ART)); - ladle.batch(calls); - - (uint128 art, uint128 ink) = cauldron.balances(vaultId); - assertEq(ink, INK); - assertEq(art, 0); - - (art, ink) = cauldron.balances(otherVaultId); - assertEq(ink, INK); - assertEq(art, ART); - } } contract BorrowedStateTests is BorrowedState { diff --git a/src/test/variable/VRWitch.t.sol b/src/test/variable/VRWitch.t.sol index f0ba1486..5c07714d 100644 --- a/src/test/variable/VRWitch.t.sol +++ b/src/test/variable/VRWitch.t.sol @@ -9,7 +9,7 @@ import "../../variable/interfaces/IVRWitch.sol"; import "../../interfaces/IWitchEvents.sol"; import "../../interfaces/IWitchErrors.sol"; import "../../variable/VRWitch.sol"; -import "openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; abstract contract WitchStateZero is Test, TestConstants, IWitchEvents, IWitchErrors { using Mocks for *; diff --git a/src/test/variable/VYToken.t.sol b/src/test/variable/VYToken.t.sol index 83df4a0d..076e07ac 100644 --- a/src/test/variable/VYToken.t.sol +++ b/src/test/variable/VYToken.t.sol @@ -7,15 +7,18 @@ contract VYTokenTest is VYTokenZeroState { // Test that the storage is initialized function testStorageInitialized() public { + IERC20Metadata token = IERC20Metadata(vyToken.underlying()); + assertTrue(vyToken.initialized()); + assertEq(token.name(), vyToken.name()); + assertEq(token.decimals(), vyToken.decimals()); + assertEq(token.symbol(), vyToken.symbol()); } // Test that the storage can't be initialized again - function testInitializeRevertsIfInitialized() public { + function testFail_InitializeRevertsIfInitialized() public { vyToken.grantRole(VYToken.initialize.selector, address(this)); - - vm.expectRevert("Already initialized"); - vyToken.initialize(address(this)); + vyToken.initialize(address(this), base.name(), base.symbol(), base.decimals()); } // Test that only authorized addresses can upgrade @@ -174,13 +177,6 @@ contract VYTokenTest is VYTokenZeroState { assertEq(unit, IERC20(vyToken.underlying()).balanceOf(user)); } - function testFlashFeeFactor() public { - console.log("can set the flash fee factor"); - assertEq(vyToken.flashFeeFactor(), type(uint256).max); - vyToken.setFlashFeeFactor(1); - assertEq(vyToken.flashFeeFactor(), 1); - } - function testFuzz_convertToUnderlyingWithIncreasingRates(uint128 newRate) public { @@ -259,75 +255,3 @@ contract VYTokenTest is VYTokenZeroState { assertLe(principalAmount, vyToken.convertToPrincipal(INK)); } } - -contract FlashLoanEnabledStateTests is FlashLoanEnabledState { - function testReturnsCorrectMaxFlashLoan() public { - console.log("can return the correct max flash loan"); - assertEq(vyToken.maxFlashLoan(address(vyToken)), type(uint256).max); - } - - function testFlashBorrow() public { - console.log("can do a simple flash borrow"); - - borrower.flashBorrow( - address(vyToken), - unit, - FlashBorrower.Action.NORMAL - ); - - assertEq(vyToken.balanceOf(address(this)), unit); - assertEq(borrower.flashBalance(), unit); - assertEq(borrower.flashToken(), address(vyToken)); - assertEq(borrower.flashAmount(), unit); - assertEq(borrower.flashInitiator(), address(borrower)); - } - - function testRepayWithTransfer() public { - vm.expectEmit(true, true, false, true); - emit Transfer(address(vyToken), address(0), unit); - - borrower.flashBorrow( - address(vyToken), - unit, - FlashBorrower.Action.TRANSFER - ); - - assertEq(vyToken.balanceOf(address(this)), unit); - assertEq(borrower.flashBalance(), unit); - assertEq(borrower.flashToken(), address(vyToken)); - assertEq(borrower.flashAmount(), unit); - assertEq(borrower.flashFee(), 0); - assertEq(borrower.flashInitiator(), address(borrower)); - } - - function testApproveNonInitiator() public { - vm.expectRevert("ERC20: Insufficient approval"); - vm.prank(address(this)); - vyToken.flashLoan( - borrower, - address(vyToken), - unit, - bytes(abi.encode(0)) - ); - } - - function testEnoughFundsForLoanRepay() public { - vm.expectRevert("ERC20: Insufficient balance"); - vm.prank(address(this)); - borrower.flashBorrow( - address(vyToken), - unit, - FlashBorrower.Action.STEAL - ); - } - - function testNestedFlashLoans() public { - borrower.flashBorrow( - address(vyToken), - unit, - FlashBorrower.Action.REENTER - ); - vm.prank(address(this)); - assertEq(borrower.flashBalance(), unit * 3); - } -} diff --git a/src/variable/VRCauldron.sol b/src/variable/VRCauldron.sol index bfc69210..3c6311e3 100644 --- a/src/variable/VRCauldron.sol +++ b/src/variable/VRCauldron.sol @@ -1,4 +1,10 @@ // SPDX-License-Identifier: BUSL-1.1 +// Audited as of 15 May 2023. +// Reports: +// https://github.com/yieldprotocol/variable-rate-audit-gogoauditor/issues/1 +// https://github.com/yieldprotocol/variable-rate-audit-parth-15/issues?q=is%3Aissue+is%3Aclosed +// https://github.com/yieldprotocol/variable-rate-audit-obheda12/issues +// https://github.com/yieldprotocol/variable-rate-audit-DecorativePineapple/issues/19 pragma solidity >=0.8.13; import "../constants/Constants.sol"; import "../interfaces/IOracle.sol"; @@ -7,7 +13,7 @@ import "@yield-protocol/utils-v2/src/access/AccessControl.sol"; import "@yield-protocol/utils-v2/src/utils/Math.sol"; import "@yield-protocol/utils-v2/src/utils/Cast.sol"; import { CauldronMath } from "../Cauldron.sol"; -import { UUPSUpgradeable } from "openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; +import { UUPSUpgradeable } from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; contract VRCauldron is UUPSUpgradeable, AccessControl, Constants { @@ -84,6 +90,7 @@ contract VRCauldron is UUPSUpgradeable, AccessControl, Constants { constructor() { // See https://medium.com/immunefi/wormhole-uninitialized-proxy-bugfix-review-90250c41a43a initialized = true; // Lock the implementation contract + _revokeRole(ROOT, msg.sender); // Remove the deployer's ROOT role } // ==== Upgradability ==== @@ -103,11 +110,12 @@ contract VRCauldron is UUPSUpgradeable, AccessControl, Constants { // ==== Administration ==== /// @dev Add a new Asset. + /// @notice Only ERC20 support has been tested thoroughly. Any other tokens would have to be tested. function addAsset(bytes6 assetId, address asset) external auth { require(assetId != bytes6(0), "Asset id is zero"); require(assets[assetId] == address(0), "Id already used"); assets[assetId] = asset; - emit AssetAdded(assetId, address(asset)); + emit AssetAdded(assetId, asset); } /// @dev Set the maximum and minimum debt for an underlying and ilk pair. Can be reset. @@ -286,12 +294,12 @@ contract VRCauldron is UUPSUpgradeable, AccessControl, Constants { external returns (uint128 art) { - art = _debtFromBase(baseId, base); + art = _baseToArt(baseId, base); } /// @dev Convert a base amount to debt terms. /// @notice Think about rounding up if using, since we are dividing. - function _debtFromBase(bytes6 baseId, uint128 base) + function _baseToArt(bytes6 baseId, uint128 base) internal returns (uint128 art) { @@ -299,16 +307,17 @@ contract VRCauldron is UUPSUpgradeable, AccessControl, Constants { art = uint256(base).wdivup(rate).u128(); } - /// @dev Convert a debt amount for a to base terms + /// @dev Convert a debt amount to base terms function debtToBase(bytes6 baseId, uint128 art) external returns (uint128 base) { - base = _debtToBase(baseId, art); + base = _artToBase(baseId, art); } - /// @dev Convert a debt amount for a to base terms - function _debtToBase(bytes6 baseId, uint128 art) + /// @dev Convert a debt amount to base terms + /// @return base is the current debt of a position, and is obtained by multiplying the normalized debt (art) by the current rate + function _artToBase(bytes6 baseId, uint128 art) internal returns (uint128 base) { @@ -366,8 +375,8 @@ contract VRCauldron is UUPSUpgradeable, AccessControl, Constants { return (balancesFrom, balancesTo); } - /// @dev Add collateral and rate from vault, pull assets from and push rateed asset to user - /// Or, repay to vault and remove collateral, pull rateed asset from and push assets to user + /// @dev Add collateral and borrow from vault, pull assets from and push borrowed asset to user + /// Or, repay to vault and remove collateral, pull borrowed asset from and push assets to user function _pour( bytes12 vaultId, VRDataTypes.Vault memory vault_, @@ -417,8 +426,8 @@ contract VRCauldron is UUPSUpgradeable, AccessControl, Constants { if (base != 0) art = base > 0 - ? _debtFromBase(vault_.baseId, base.u128()).i128() - : -_debtFromBase(vault_.baseId, (-base).u128()).i128(); + ? _baseToArt(vault_.baseId, base.u128()).i128() + : -_baseToArt(vault_.baseId, (-base).u128()).i128(); balances_ = _pour(vaultId, vault_, balances_, ink, art); @@ -441,7 +450,7 @@ contract VRCauldron is UUPSUpgradeable, AccessControl, Constants { ) = vaultData(vaultId); // Normalize the base amount to debt terms - int128 art = _debtFromBase(vault_.baseId, base).i128(); + int128 art = _baseToArt(vault_.baseId, base).i128(); balances_ = _pour(vaultId, vault_, balances_, -(ink.i128()), -art); @@ -474,7 +483,7 @@ contract VRCauldron is UUPSUpgradeable, AccessControl, Constants { vault_.baseId, balances_.ink ); // ink * spot - uint256 baseValue = _debtToBase(vault_.baseId, balances_.art); // art * rate + uint256 baseValue = _artToBase(vault_.baseId, balances_.art); // art * rate return inkValue.i256() - baseValue.wmul(ratio).i256(); } } \ No newline at end of file diff --git a/src/variable/VRLadle.sol b/src/variable/VRLadle.sol index 58f2374c..5f247814 100644 --- a/src/variable/VRLadle.sol +++ b/src/variable/VRLadle.sol @@ -1,4 +1,10 @@ // SPDX-License-Identifier: BUSL-1.1 +// Audited as of 15 May 2023. +// Reports: +// https://github.com/yieldprotocol/variable-rate-audit-gogoauditor/issues/1 +// https://github.com/yieldprotocol/variable-rate-audit-parth-15/issues?q=is%3Aissue+is%3Aclosed +// https://github.com/yieldprotocol/variable-rate-audit-obheda12/issues +// https://github.com/yieldprotocol/variable-rate-audit-DecorativePineapple/issues/19 pragma solidity >=0.8.13; import "../interfaces/IFYToken.sol"; import "../interfaces/IJoin.sol"; @@ -15,10 +21,10 @@ import "@yield-protocol/utils-v2/src/token/TransferHelper.sol"; import "@yield-protocol/utils-v2/src/utils/Math.sol"; import "@yield-protocol/utils-v2/src/utils/Cast.sol"; import "dss-interfaces/src/dss/DaiAbstract.sol"; -import { UUPSUpgradeable } from "openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; +import { UUPSUpgradeable } from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; /// @dev Ladle orchestrates contract calls throughout the Yield Protocol v2 into useful and efficient user oriented features. -contract VRLadle is UUPSUpgradeable, AccessControl() { +contract VRLadle is UUPSUpgradeable, AccessControl { using Math for uint256; using Cast for uint256; using Cast for uint128; @@ -48,6 +54,7 @@ contract VRLadle is UUPSUpgradeable, AccessControl() { // See https://medium.com/immunefi/wormhole-uninitialized-proxy-bugfix-review-90250c41a43a initialized = true; // Lock the implementation contract + _revokeRole(ROOT, msg.sender); // Remove the deployer's ROOT role } // ---- Upgradability ---- @@ -119,8 +126,7 @@ contract VRLadle is UUPSUpgradeable, AccessControl() { require(join.asset() == asset, "Mismatched asset and join"); joins[assetId] = join; - bool set = (join != IJoin(address(0))) ? true : false; - _addToken(asset, set); // address(0) disables the token + _addToken(asset, true); emit JoinAdded(assetId, address(join)); } @@ -217,7 +223,6 @@ contract VRLadle is UUPSUpgradeable, AccessControl() { /// @dev Accept Ether, wrap it and forward it to the provided address /// This function should be called first in a batch, and the Join should keep track of stored reserves - /// Passing the id for a join that doesn't link to a contract implemnting IWETH9 will fail function wrapEther( address to ) external payable returns (uint256 ethTransferred) { @@ -297,26 +302,6 @@ contract VRLadle is UUPSUpgradeable, AccessControl() { // ---- Asset and debt management ---- - /// @dev Move collateral and debt between vaults. - function stir( - bytes12 from, - bytes12 to, - uint128 ink, - uint128 art - ) external payable { - if (ink > 0) - require( - cauldron.vaults(from).owner == msg.sender, - "Only origin vault owner" - ); - if (art > 0) - require( - cauldron.vaults(to).owner == msg.sender, - "Only destination vault owner" - ); - cauldron.stir(from, to, ink, art); - } - /// @dev Add collateral and borrow from vault, pull assets from and push borrowed asset to user /// Or, repay to vault and remove collateral, pull borrowed asset from and push assets to user /// Borrow only before maturity. @@ -338,20 +323,19 @@ contract VRLadle is UUPSUpgradeable, AccessControl() { if (ink != 0) { IJoin ilkJoin = getJoin(vault.ilkId); if (ink > 0) ilkJoin.join(vault.owner, uint128(ink)); - if (ink < 0) ilkJoin.exit(to, uint128(-ink)); + else ilkJoin.exit(to, uint128(-ink)); } // Manage base if (base != 0) { IJoin baseJoin = getJoin(vault.baseId); if (base < 0) baseJoin.join(vault.owner, uint128(-base)); - if (base > 0) baseJoin.exit(to, uint128(base)); + else baseJoin.exit(to, uint128(base)); } } /// @dev Add collateral and borrow from vault, pull assets from and push borrowed asset to user /// Or, repay to vault and remove collateral, pull borrowed asset from and push assets to user - /// Borrow only before maturity. function pour( bytes12 vaultId_, address to, @@ -363,7 +347,8 @@ contract VRLadle is UUPSUpgradeable, AccessControl() { } /// @dev Repay all debt in a vault. - /// The base tokens need to be already in the join, unaccounted for. The surplus base will be returned to msg.sender. + /// The base tokens need to be already in the join, unaccounted for. + /// The surplus base will be returned to refundTo address, if refundTo is different than address(0). function repay( bytes12 vaultId_, address inkTo, diff --git a/src/variable/VRRouter.sol b/src/variable/VRRouter.sol index f418033d..6a23851a 100644 --- a/src/variable/VRRouter.sol +++ b/src/variable/VRRouter.sol @@ -1,4 +1,10 @@ // SPDX-License-Identifier: BUSL-1.1 +// Audited as of 15 May 2023. +// Reports: +// https://github.com/yieldprotocol/variable-rate-audit-gogoauditor/issues/1 +// https://github.com/yieldprotocol/variable-rate-audit-parth-15/issues?q=is%3Aissue+is%3Aclosed +// https://github.com/yieldprotocol/variable-rate-audit-obheda12/issues +// https://github.com/yieldprotocol/variable-rate-audit-DecorativePineapple/issues/19 pragma solidity >=0.8.13; import "@yield-protocol/utils-v2/src/utils/RevertMsgExtractor.sol"; import "@yield-protocol/utils-v2/src/utils/IsContract.sol"; diff --git a/src/variable/VRWitch.sol b/src/variable/VRWitch.sol index a1832e25..12af6065 100644 --- a/src/variable/VRWitch.sol +++ b/src/variable/VRWitch.sol @@ -1,10 +1,16 @@ // SPDX-License-Identifier: BUSL-1.1 +// Audited as of 15 May 2023. +// Reports: +// https://github.com/yieldprotocol/variable-rate-audit-gogoauditor/issues/1 +// https://github.com/yieldprotocol/variable-rate-audit-parth-15/issues?q=is%3Aissue+is%3Aclosed +// https://github.com/yieldprotocol/variable-rate-audit-obheda12/issues +// https://github.com/yieldprotocol/variable-rate-audit-DecorativePineapple/issues/19 pragma solidity >=0.8.13; import "@yield-protocol/utils-v2/src/utils/Cast.sol"; import "./interfaces/IVRCauldron.sol"; import "../WitchBase.sol"; -import { UUPSUpgradeable } from "openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; +import { UUPSUpgradeable } from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; /// @title The Witch is a DataTypes.Auction/Liquidation Engine for the Yield protocol /// @notice The Witch grabs under-collateralised vaults, replacing the owner by itself. Then it sells @@ -23,6 +29,7 @@ contract VRWitch is WitchBase, UUPSUpgradeable { constructor(ICauldron cauldron_, ILadle ladle_) WitchBase(cauldron_, ladle_) { // See https://medium.com/immunefi/wormhole-uninitialized-proxy-bugfix-review-90250c41a43a initialized = true; // Lock the implementation contract + _revokeRole(ROOT, msg.sender); // Remove the deployer's ROOT role } // ====================================================================== diff --git a/src/variable/VYToken.sol b/src/variable/VYToken.sol index d4b05173..5ce32da2 100644 --- a/src/variable/VYToken.sol +++ b/src/variable/VYToken.sol @@ -1,8 +1,13 @@ // SPDX-License-Identifier: BUSL-1.1 +// Audited as of 15 May 2023. +// Reports: +// https://github.com/yieldprotocol/variable-rate-audit-gogoauditor/issues/1 +// https://github.com/yieldprotocol/variable-rate-audit-parth-15/issues?q=is%3Aissue+is%3Aclosed +// https://github.com/yieldprotocol/variable-rate-audit-obheda12/issues +// https://github.com/yieldprotocol/variable-rate-audit-DecorativePineapple/issues/19 + pragma solidity >=0.8.13; -import "erc3156/contracts/interfaces/IERC3156FlashBorrower.sol"; -import "erc3156/contracts/interfaces/IERC3156FlashLender.sol"; import "@yield-protocol/utils-v2/src/token/ERC20Permit.sol"; import "@yield-protocol/utils-v2/src/token/SafeERC20Namer.sol"; import "@yield-protocol/utils-v2/src/access/AccessControl.sol"; @@ -11,22 +16,17 @@ import "@yield-protocol/utils-v2/src/utils/Cast.sol"; import "../interfaces/IJoin.sol"; import "../interfaces/IOracle.sol"; import "../constants/Constants.sol"; -import { UUPSUpgradeable } from "openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; +import { UUPSUpgradeable } from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol"; -contract VYToken is IERC3156FlashLender, UUPSUpgradeable, AccessControl, ERC20Permit, Constants { +contract VYToken is UUPSUpgradeable, AccessControl, ERC20Permit, Constants { using Math for uint256; using Cast for uint256; - event Point(bytes32 indexed param, address value); - event FlashFeeFactorSet(uint256 indexed fee); event Redeemed(address indexed holder, address indexed receiver, uint256 principalAmount, uint256 underlyingAmount); + event Deposited(address indexed sender,address indexed receiver, uint256 underlyingAmount, uint256 principalAmount); bool public initialized; - bytes32 internal constant FLASH_LOAN_RETURN = keccak256("ERC3156FlashBorrower.onFlashLoan"); - uint256 constant FLASH_LOANS_DISABLED = type(uint256).max; - uint256 public flashFeeFactor = FLASH_LOANS_DISABLED; // Fee on flash loans, as a percentage in fixed point with 18 decimals. Flash loans disabled by default by overflow from `flashFee`. - IOracle public immutable oracle; // Oracle for the savings rate. IJoin public immutable join; // Source of redemption funds. address public immutable underlying; @@ -47,27 +47,24 @@ contract VYToken is IERC3156FlashLender, UUPSUpgradeable, AccessControl, ERC20Pe // See https://medium.com/immunefi/wormhole-uninitialized-proxy-bugfix-review-90250c41a43a initialized = true; // Lock the implementation contract + _revokeRole(ROOT, msg.sender); // Remove the deployer's ROOT role } /// @dev Give the ROOT role and create a LOCK role with itself as the admin role and no members. /// Calling setRoleAdmin(msg.sig, LOCK) means no one can grant that msg.sig role anymore. - function initialize (address root_) public { + function initialize (address root_, string memory name_, string memory symbol_, uint8 decimals_) public { require(!initialized, "Already initialized"); initialized = true; // On an uninitialized contract, no governance functions can be executed, because no one has permission to do so _grantRole(ROOT, root_); // Grant ROOT _setRoleAdmin(LOCK, LOCK); // Create the LOCK role by setting itself as its own admin, creating an independent role tree - flashFeeFactor = FLASH_LOANS_DISABLED; // Flash loans disabled by default + name = name_; + symbol = symbol_; + decimals = decimals_; } /// @dev Allow to set a new implementation function _authorizeUpgrade(address newImplementation) internal override auth {} - /// @dev Set the flash loan fee factor - function setFlashFeeFactor(uint256 flashFeeFactor_) external auth { - flashFeeFactor = flashFeeFactor_; - emit FlashFeeFactorSet(flashFeeFactor_); - } - ///@dev Converts the amount of the principal to the underlying function convertToUnderlying(uint256 principalAmount) external returns (uint256 underlyingAmount) { return _convertToUnderlying(principalAmount); @@ -162,7 +159,11 @@ contract VYToken is IERC3156FlashLender, UUPSUpgradeable, AccessControl, ERC20Pe /// @dev Mint vyTokens. function deposit(address receiver, uint256 underlyingAmount) external auth { join.join(msg.sender, underlyingAmount.u128()); - _mint(receiver, _convertToPrincipal(underlyingAmount)); + + uint256 principalAmount = _convertToPrincipal(underlyingAmount); + _mint(receiver, principalAmount); + + emit Deposited(msg.sender, receiver, underlyingAmount, principalAmount); } ///@dev returns the maximum depositable amount for the address holder in terms of the underlying @@ -193,59 +194,4 @@ contract VYToken is IERC3156FlashLender, UUPSUpgradeable, AccessControl, ERC20Pe } } } - - /** - * @dev From ERC-3156. The amount of currency available to be lended. - * @param token The loan currency. It must be a VYToken contract. - * @return The amount of `token` that can be borrowed. - */ - function maxFlashLoan(address token) external view returns (uint256) { - return token == address(this) ? type(uint256).max - _totalSupply : 0; - } - - /** - * @dev From ERC-3156. The fee to be charged for a given loan. - * @param token The loan currency. It must be the asset. - * @param principalAmount The amount of tokens lent. - * @return The amount of `token` to be charged for the loan, on top of the returned principal. - */ - function flashFee(address token, uint256 principalAmount) external view returns (uint256) { - require(token == address(this), "Unsupported currency"); - return _flashFee(principalAmount); - } - - /** - * @dev The fee to be charged for a given loan. - * @param principalAmount The amount of tokens lent. - * @return The amount of `token` to be charged for the loan, on top of the returned principal. - */ - function _flashFee(uint256 principalAmount) internal view returns (uint256) { - return principalAmount.wmul(flashFeeFactor); - } - - /** - * @dev From ERC-3156. Loan `amount` vyDai to `receiver`, which needs to return them plus fee to this contract within the same transaction. - * Note that if the initiator and the borrower are the same address, no approval is needed for this contract to take the principal + fee from the borrower. - * If the borrower transfers the principal + fee to this contract, they will be burnt here instead of pulled from the borrower. - * @param receiver The contract receiving the tokens, needs to implement the `onFlashLoan(address user, uint256 amount, uint256 fee, bytes calldata)` interface. - * @param token The loan currency. Must be a vyDai contract. - * @param principalAmount The amount of tokens lent. - * @param data A data parameter to be passed on to the `receiver` for any custom use. - */ - function flashLoan( - IERC3156FlashBorrower receiver, - address token, - uint256 principalAmount, - bytes memory data - ) external returns (bool) { - require(token == address(this), "Unsupported currency"); - _mint(address(receiver), principalAmount); - uint128 fee = _flashFee(principalAmount).u128(); - require( - receiver.onFlashLoan(msg.sender, token, principalAmount, fee, data) == FLASH_LOAN_RETURN, - "Non-compliant borrower" - ); - _burn(address(receiver), principalAmount + fee); - return true; - } } \ No newline at end of file