Files
30
Total Lines
3914
Coverage
80.0%
583 / 726 lines
Actions
66.0%
contracts/contracts/echidna/Debugger.sol
Lines covered: 4 / 6 (66.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | library Debugger { |
| 5 | event Debug(string debugString); |
| 6 | event Debug(string description, string data); |
| 7 | event Debug(string prefix, string description, string data); |
| 8 | event Debug(string description, bytes32 data); |
| 9 | event Debug(string prefix, string description, bytes32 data); |
| 10 | event Debug(string description, uint256 data); |
| 11 | event Debug(string prefix, string description, uint256 data); |
| 12 | event Debug(string description, int256 data); |
| 13 | event Debug(string prefix, string description, int256 data); |
| 14 | event Debug(string description, address data); |
| 15 | event Debug(string prefix, string description, address data); |
| 16 | event Debug(string description, bool data); |
| 17 | event Debug(string prefix, string description, bool data); |
| 18 | |
| 19 | function log(string memory debugString) internal { |
| 20 | emit Debug(debugString); |
| 21 | } |
| 22 | |
| 23 | function log(string memory description, string memory data) internal { |
| 24 | emit Debug(description, data); |
| 25 | } |
| 26 | |
| 27 | function log( |
| 28 | string memory prefix, |
| 29 | string memory description, |
| 30 | string memory data |
| 31 | ) internal { |
| 32 | emit Debug(prefix, description, data); |
| 33 | } |
| 34 | |
| 35 | function log(string memory description, bytes32 data) internal { |
| 36 | emit Debug(description, data); |
| 37 | } |
| 38 | |
| 39 | function log( |
| 40 | string memory prefix, |
| 41 | string memory description, |
| 42 | bytes32 data |
| 43 | ) internal { |
| 44 | emit Debug(prefix, description, data); |
| 45 | } |
| 46 | |
| 47 | function log(string memory description, uint256 data) internal { |
| 48 | emit Debug(description, data); |
| 49 | } |
| 50 | |
| 51 | function log( |
| 52 | string memory prefix, |
| 53 | string memory description, |
| 54 | uint256 data |
| 55 | ) internal { |
| 56 | emit Debug(prefix, description, data); |
| 57 | } |
| 58 | |
| 59 | function log(string memory description, int256 data) internal { |
| 60 | emit Debug(description, data); |
| 61 | } |
| 62 | |
| 63 | function log( |
| 64 | string memory prefix, |
| 65 | string memory description, |
| 66 | int256 data |
| 67 | ) internal { |
| 68 | emit Debug(prefix, description, data); |
| 69 | } |
| 70 | |
| 71 | function log(string memory description, address data) internal { |
| 72 | emit Debug(description, data); |
| 73 | } |
| 74 | |
| 75 | function log( |
| 76 | string memory prefix, |
| 77 | string memory description, |
| 78 | address data |
| 79 | ) internal { |
| 80 | emit Debug(prefix, description, data); |
| 81 | } |
| 82 | |
| 83 | function log(string memory description, bool data) internal { |
| 84 | emit Debug(description, data); |
| 85 | } |
| 86 | |
| 87 | function log( |
| 88 | string memory prefix, |
| 89 | string memory description, |
| 90 | bool data |
| 91 | ) internal { |
| 92 | emit Debug(prefix, description, data); |
| 93 | } |
| 94 | } |
| 95 |
0.0%
contracts/contracts/echidna/Echidna.sol
Lines covered: 0 / 1 (0.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | import "./EchidnaTestApproval.sol"; |
| 5 | |
| 6 | /** |
| 7 | * @title Echidna test contract for OUSD |
| 8 | * @notice Target contract to be tested, containing all mixins |
| 9 | * @author Rappie |
| 10 | */ |
| 11 | contract Echidna is EchidnaTestApproval { |
| 12 | |
| 13 | } |
| 14 |
100.0%
contracts/contracts/echidna/EchidnaConfig.sol
Lines covered: 20 / 20 (100.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | /** |
| 5 | * @title Top-level mixin for configuring the desired fuzzing setup |
| 6 | * @author Rappie |
| 7 | */ |
| 8 | contract EchidnaConfig { |
| 9 | address internal constant ADDRESS_VAULT = address(0x10000); |
| 10 | address internal constant ADDRESS_OUTSIDER_USER = address(0x20000); |
| 11 | |
| 12 | address internal constant ADDRESS_USER0 = address(0x30000); |
| 13 | address internal constant ADDRESS_USER1 = address(0x40000); |
| 14 | |
| 15 | // Will be set in EchidnaSetup constructor |
| 16 | address internal ADDRESS_OUTSIDER_CONTRACT; |
| 17 | address internal ADDRESS_CONTRACT0; |
| 18 | address internal ADDRESS_CONTRACT1; |
| 19 | |
| 20 | // Toggle known issues |
| 21 | // |
| 22 | // This can be used to skip tests that are known to fail. This is useful |
| 23 | // when debugging a specific issue, but should be disabled when running |
| 24 | // the full test suite. |
| 25 | // |
| 26 | // True => skip tests that are known to fail |
| 27 | // False => run all tests |
| 28 | // |
| 29 | bool internal constant TOGGLE_KNOWN_ISSUES = false; |
| 30 | |
| 31 | // Toggle known issues within limits |
| 32 | // |
| 33 | // Same as TOGGLE_KNOWN_ISSUES, but also skip tests that are known to fail |
| 34 | // within limits set by the variables below. |
| 35 | // |
| 36 | bool internal constant TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS = false; |
| 37 | |
| 38 | // Starting balance |
| 39 | // |
| 40 | // Gives OUSD a non-zero starting supply, which can be useful to ignore |
| 41 | // certain edge cases. |
| 42 | // |
| 43 | // The starting balance is given to outsider accounts that are not used as |
| 44 | // accounts while fuzzing. |
| 45 | // |
| 46 | bool internal constant TOGGLE_STARTING_BALANCE = true; |
| 47 | uint256 internal constant STARTING_BALANCE = 1_000_000e18; |
| 48 | |
| 49 | // Change supply |
| 50 | // |
| 51 | // Set a limit to the amount of change per rebase, which can be useful to |
| 52 | // ignore certain edge cases. |
| 53 | // |
| 54 | // True => limit the amount of change to a percentage of total supply |
| 55 | // False => no limit |
| 56 | // |
| 57 | bool internal constant TOGGLE_CHANGESUPPLY_LIMIT = true; |
| 58 | uint256 internal constant CHANGESUPPLY_DIVISOR = 10; // 10% of total supply |
| 59 | |
| 60 | // Mint limit |
| 61 | // |
| 62 | // Set a limit the amount minted per mint, which can be useful to |
| 63 | // ignore certain edge cases. |
| 64 | // |
| 65 | // True => limit the amount of minted tokens |
| 66 | // False => no limit |
| 67 | // |
| 68 | bool internal constant TOGGLE_MINT_LIMIT = true; |
| 69 | uint256 internal constant MINT_MODULO = 1_000_000_000_000e18; |
| 70 | |
| 71 | // Known rounding errors |
| 72 | uint256 internal constant TRANSFER_ROUNDING_ERROR = 1e18 - 1; |
| 73 | uint256 internal constant OPT_IN_ROUNDING_ERROR = 1e18 - 1; |
| 74 | uint256 internal constant MINT_ROUNDING_ERROR = 1e18 - 1; |
| 75 | |
| 76 | /** |
| 77 | * @notice Modifier to skip tests that are known to fail |
| 78 | * @dev see TOGGLE_KNOWN_ISSUES for more information |
| 79 | */ |
| 80 | modifier hasKnownIssue() { |
| 81 | if (TOGGLE_KNOWN_ISSUES) return; |
| 82 | _; |
| 83 | } |
| 84 | |
| 85 | /** |
| 86 | * @notice Modifier to skip tests that are known to fail within limits |
| 87 | * @dev see TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS for more information |
| 88 | */ |
| 89 | modifier hasKnownIssueWithinLimits() { |
| 90 | if (TOGGLE_KNOWN_ISSUES_WITHIN_LIMITS) return; |
| 91 | _; |
| 92 | } |
| 93 | |
| 94 | /** |
| 95 | * @notice Translate an account ID to an address |
| 96 | * @param accountId The ID of the account |
| 97 | * @return account The address of the account |
| 98 | */ |
| 99 | function getAccount(uint8 accountId) |
| 100 | internal |
| 101 | view |
| 102 | returns (address account) |
| 103 | { |
| 104 | accountId = accountId / 64; |
| 105 | if (accountId == 0) return account = ADDRESS_USER0; |
| 106 | if (accountId == 1) return account = ADDRESS_USER1; |
| 107 | if (accountId == 2) return account = ADDRESS_CONTRACT0; |
| 108 | if (accountId == 3) return account = ADDRESS_CONTRACT1; |
| 109 | require(false, "Unknown account ID"); |
| 110 | } |
| 111 | } |
| 112 |
0.0%
contracts/contracts/echidna/EchidnaDebug.sol
Lines covered: 0 / 1 (0.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | import { Address } from "@openzeppelin/contracts/utils/Address.sol"; |
| 5 | |
| 6 | import "./EchidnaHelper.sol"; |
| 7 | import "./Debugger.sol"; |
| 8 | |
| 9 | import "../token/OUSD.sol"; |
| 10 | |
| 11 | /** |
| 12 | * @title Room for random debugging functions |
| 13 | * @author Rappie |
| 14 | */ |
| 15 | contract EchidnaDebug is EchidnaHelper { |
| 16 | function debugOUSD() public pure { |
| 17 | // assert(ousd.balanceOf(ADDRESS_USER0) == 1000); |
| 18 | // assert(ousd.rebaseState(ADDRESS_USER0) != OUSD.RebaseOptions.OptIn); |
| 19 | // assert(Address.isContract(ADDRESS_CONTRACT0)); |
| 20 | // Debugger.log("nonRebasingSupply", ousd.nonRebasingSupply()); |
| 21 | // assert(false); |
| 22 | } |
| 23 | } |
| 24 |
95.0%
contracts/contracts/echidna/EchidnaHelper.sol
Lines covered: 70 / 73 (95.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | import "./EchidnaSetup.sol"; |
| 5 | import "./Debugger.sol"; |
| 6 | |
| 7 | /** |
| 8 | * @title Mixin containing helper functions |
| 9 | * @author Rappie |
| 10 | */ |
| 11 | contract EchidnaHelper is EchidnaSetup { |
| 12 | /** |
| 13 | * @notice Mint tokens to an account |
| 14 | * @param toAcc Account to mint to |
| 15 | * @param amount Amount to mint |
| 16 | * @return Amount minted (in case of capped mint with modulo) |
| 17 | */ |
| 18 | function mint(uint8 toAcc, uint256 amount) public returns (uint256) { |
| 19 | address to = getAccount(toAcc); |
| 20 | |
| 21 | if (TOGGLE_MINT_LIMIT) { |
| 22 | amount = amount % MINT_MODULO; |
| 23 | } |
| 24 | |
| 25 | hevm.prank(ADDRESS_VAULT); |
| 26 | ousd.mint(to, amount); |
| 27 | |
| 28 | return amount; |
| 29 | } |
| 30 | |
| 31 | /** |
| 32 | * @notice Burn tokens from an account |
| 33 | * @param fromAcc Account to burn from |
| 34 | * @param amount Amount to burn |
| 35 | */ |
| 36 | function burn(uint8 fromAcc, uint256 amount) public { |
| 37 | address from = getAccount(fromAcc); |
| 38 | hevm.prank(ADDRESS_VAULT); |
| 39 | ousd.burn(from, amount); |
| 40 | } |
| 41 | |
| 42 | /** |
| 43 | * @notice Change the total supply of OUSD (rebase) |
| 44 | * @param amount New total supply |
| 45 | */ |
| 46 | function changeSupply(uint256 amount) public { |
| 47 | if (TOGGLE_CHANGESUPPLY_LIMIT) { |
| 48 | amount = |
| 49 | ousd.totalSupply() + |
| 50 | (amount % (ousd.totalSupply() / CHANGESUPPLY_DIVISOR)); |
| 51 | } |
| 52 | |
| 53 | hevm.prank(ADDRESS_VAULT); |
| 54 | ousd.changeSupply(amount); |
| 55 | } |
| 56 | |
| 57 | /** |
| 58 | * @notice Transfer tokens between accounts |
| 59 | * @param fromAcc Account to transfer from |
| 60 | * @param toAcc Account to transfer to |
| 61 | * @param amount Amount to transfer |
| 62 | */ |
| 63 | function transfer( |
| 64 | uint8 fromAcc, |
| 65 | uint8 toAcc, |
| 66 | uint256 amount |
| 67 | ) public { |
| 68 | address from = getAccount(fromAcc); |
| 69 | address to = getAccount(toAcc); |
| 70 | hevm.prank(from); |
| 71 | // slither-disable-next-line unchecked-transfer |
| 72 | ousd.transfer(to, amount); |
| 73 | } |
| 74 | |
| 75 | /** |
| 76 | * @notice Transfer approved tokens between accounts |
| 77 | * @param authorizedAcc Account that is authorized to transfer |
| 78 | * @param fromAcc Account to transfer from |
| 79 | * @param toAcc Account to transfer to |
| 80 | * @param amount Amount to transfer |
| 81 | */ |
| 82 | function transferFrom( |
| 83 | uint8 authorizedAcc, |
| 84 | uint8 fromAcc, |
| 85 | uint8 toAcc, |
| 86 | uint256 amount |
| 87 | ) public { |
| 88 | address authorized = getAccount(authorizedAcc); |
| 89 | address from = getAccount(fromAcc); |
| 90 | address to = getAccount(toAcc); |
| 91 | hevm.prank(authorized); |
| 92 | // slither-disable-next-line unchecked-transfer |
| 93 | ousd.transferFrom(from, to, amount); |
| 94 | } |
| 95 | |
| 96 | /** |
| 97 | * @notice Opt in to rebasing |
| 98 | * @param targetAcc Account to opt in |
| 99 | */ |
| 100 | function optIn(uint8 targetAcc) public { |
| 101 | address target = getAccount(targetAcc); |
| 102 | hevm.prank(target); |
| 103 | ousd.rebaseOptIn(); |
| 104 | } |
| 105 | |
| 106 | /** |
| 107 | * @notice Opt out of rebasing |
| 108 | * @param targetAcc Account to opt out |
| 109 | */ |
| 110 | function optOut(uint8 targetAcc) public { |
| 111 | address target = getAccount(targetAcc); |
| 112 | hevm.prank(target); |
| 113 | ousd.rebaseOptOut(); |
| 114 | } |
| 115 | |
| 116 | /** |
| 117 | * @notice Approve an account to spend OUSD |
| 118 | * @param ownerAcc Account that owns the OUSD |
| 119 | * @param spenderAcc Account that is approved to spend the OUSD |
| 120 | * @param amount Amount to approve |
| 121 | */ |
| 122 | function approve( |
| 123 | uint8 ownerAcc, |
| 124 | uint8 spenderAcc, |
| 125 | uint256 amount |
| 126 | ) public { |
| 127 | address owner = getAccount(ownerAcc); |
| 128 | address spender = getAccount(spenderAcc); |
| 129 | hevm.prank(owner); |
| 130 | // slither-disable-next-line unused-return |
| 131 | ousd.approve(spender, amount); |
| 132 | } |
| 133 | |
| 134 | /** |
| 135 | * @notice Get the sum of all OUSD balances |
| 136 | * @return total Total balance |
| 137 | */ |
| 138 | function getTotalBalance() public view returns (uint256 total) { |
| 139 | total += ousd.balanceOf(ADDRESS_VAULT); |
| 140 | total += ousd.balanceOf(ADDRESS_OUTSIDER_USER); |
| 141 | total += ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT); |
| 142 | total += ousd.balanceOf(ADDRESS_USER0); |
| 143 | total += ousd.balanceOf(ADDRESS_USER1); |
| 144 | total += ousd.balanceOf(ADDRESS_CONTRACT0); |
| 145 | total += ousd.balanceOf(ADDRESS_CONTRACT1); |
| 146 | } |
| 147 | |
| 148 | /** |
| 149 | * @notice Get the sum of all non-rebasing OUSD balances |
| 150 | * @return total Total balance |
| 151 | */ |
| 152 | function getTotalNonRebasingBalance() public returns (uint256 total) { |
| 153 | total += ousd._isNonRebasingAccountEchidna(ADDRESS_VAULT) |
| 154 | ? ousd.balanceOf(ADDRESS_VAULT) |
| 155 | : 0; |
| 156 | total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_USER) |
| 157 | ? ousd.balanceOf(ADDRESS_OUTSIDER_USER) |
| 158 | : 0; |
| 159 | total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_CONTRACT) |
| 160 | ? ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT) |
| 161 | : 0; |
| 162 | total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER0) |
| 163 | ? ousd.balanceOf(ADDRESS_USER0) |
| 164 | : 0; |
| 165 | total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER1) |
| 166 | ? ousd.balanceOf(ADDRESS_USER1) |
| 167 | : 0; |
| 168 | total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT0) |
| 169 | ? ousd.balanceOf(ADDRESS_CONTRACT0) |
| 170 | : 0; |
| 171 | total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT1) |
| 172 | ? ousd.balanceOf(ADDRESS_CONTRACT1) |
| 173 | : 0; |
| 174 | } |
| 175 | } |
| 176 |
100.0%
contracts/contracts/echidna/EchidnaSetup.sol
Lines covered: 16 / 16 (100.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | import "./IHevm.sol"; |
| 5 | import "./EchidnaConfig.sol"; |
| 6 | import "./OUSDEchidna.sol"; |
| 7 | |
| 8 | contract Dummy {} |
| 9 | |
| 10 | /** |
| 11 | * @title Mixin for setup and deployment |
| 12 | * @author Rappie |
| 13 | */ |
| 14 | contract EchidnaSetup is EchidnaConfig { |
| 15 | IHevm hevm = IHevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); |
| 16 | OUSDEchidna ousd = new OUSDEchidna(); |
| 17 | |
| 18 | /** |
| 19 | * @notice Deploy the OUSD contract and set up initial state |
| 20 | */ |
| 21 | constructor() { |
| 22 | ousd.initialize(ADDRESS_VAULT, 1e18); |
| 23 | |
| 24 | // Deploy dummny contracts as users |
| 25 | Dummy outsider = new Dummy(); |
| 26 | ADDRESS_OUTSIDER_CONTRACT = address(outsider); |
| 27 | Dummy dummy0 = new Dummy(); |
| 28 | ADDRESS_CONTRACT0 = address(dummy0); |
| 29 | Dummy dummy1 = new Dummy(); |
| 30 | ADDRESS_CONTRACT1 = address(dummy1); |
| 31 | |
| 32 | // Start out with a reasonable amount of OUSD |
| 33 | if (TOGGLE_STARTING_BALANCE) { |
| 34 | // Rebasing tokens |
| 35 | hevm.prank(ADDRESS_VAULT); |
| 36 | ousd.mint(ADDRESS_OUTSIDER_USER, STARTING_BALANCE / 2); |
| 37 | |
| 38 | // Non-rebasing tokens |
| 39 | hevm.prank(ADDRESS_VAULT); |
| 40 | ousd.mint(ADDRESS_OUTSIDER_CONTRACT, STARTING_BALANCE / 2); |
| 41 | } |
| 42 | } |
| 43 | } |
| 44 |
92.0%
contracts/contracts/echidna/EchidnaTestAccounting.sol
Lines covered: 37 / 40 (92.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | import "./EchidnaDebug.sol"; |
| 5 | import "./EchidnaTestSupply.sol"; |
| 6 | |
| 7 | /** |
| 8 | * @title Mixin for testing accounting functions |
| 9 | * @author Rappie |
| 10 | */ |
| 11 | contract EchidnaTestAccounting is EchidnaTestSupply { |
| 12 | /** |
| 13 | * @notice After opting in, balance should not increase. (Ok to lose rounding funds doing this) |
| 14 | * @param targetAcc Account to opt in |
| 15 | */ |
| 16 | function testOptInBalance(uint8 targetAcc) public { |
| 17 | address target = getAccount(targetAcc); |
| 18 | |
| 19 | uint256 balanceBefore = ousd.balanceOf(target); |
| 20 | optIn(targetAcc); |
| 21 | uint256 balanceAfter = ousd.balanceOf(target); |
| 22 | |
| 23 | assert(balanceAfter <= balanceBefore); |
| 24 | } |
| 25 | |
| 26 | /** |
| 27 | * @notice After opting out, balance should remain the same |
| 28 | * @param targetAcc Account to opt out |
| 29 | */ |
| 30 | function testOptOutBalance(uint8 targetAcc) public { |
| 31 | address target = getAccount(targetAcc); |
| 32 | |
| 33 | uint256 balanceBefore = ousd.balanceOf(target); |
| 34 | optOut(targetAcc); |
| 35 | uint256 balanceAfter = ousd.balanceOf(target); |
| 36 | |
| 37 | assert(balanceAfter == balanceBefore); |
| 38 | } |
| 39 | |
| 40 | /** |
| 41 | * @notice Account balance should remain the same after opting in minus rounding error |
| 42 | * @param targetAcc Account to opt in |
| 43 | */ |
| 44 | function testOptInBalanceRounding(uint8 targetAcc) public { |
| 45 | address target = getAccount(targetAcc); |
| 46 | |
| 47 | uint256 balanceBefore = ousd.balanceOf(target); |
| 48 | optIn(targetAcc); |
| 49 | uint256 balanceAfter = ousd.balanceOf(target); |
| 50 | |
| 51 | int256 delta = int256(balanceAfter) - int256(balanceBefore); |
| 52 | Debugger.log("delta", delta); |
| 53 | |
| 54 | // slither-disable-next-line tautology |
| 55 | assert(-1 * delta >= 0); |
| 56 | assert(-1 * delta <= int256(OPT_IN_ROUNDING_ERROR)); |
| 57 | } |
| 58 | |
| 59 | /** |
| 60 | * @notice After opting in, total supply should remain the same |
| 61 | * @param targetAcc Account to opt in |
| 62 | */ |
| 63 | function testOptInTotalSupply(uint8 targetAcc) public { |
| 64 | uint256 totalSupplyBefore = ousd.totalSupply(); |
| 65 | optIn(targetAcc); |
| 66 | uint256 totalSupplyAfter = ousd.totalSupply(); |
| 67 | |
| 68 | assert(totalSupplyAfter == totalSupplyBefore); |
| 69 | } |
| 70 | |
| 71 | /** |
| 72 | * @notice After opting out, total supply should remain the same |
| 73 | * @param targetAcc Account to opt out |
| 74 | */ |
| 75 | function testOptOutTotalSupply(uint8 targetAcc) public { |
| 76 | uint256 totalSupplyBefore = ousd.totalSupply(); |
| 77 | optOut(targetAcc); |
| 78 | uint256 totalSupplyAfter = ousd.totalSupply(); |
| 79 | |
| 80 | assert(totalSupplyAfter == totalSupplyBefore); |
| 81 | } |
| 82 | |
| 83 | /** |
| 84 | * @notice Account balance should remain the same when a smart contract auto converts |
| 85 | * @param targetAcc Account to auto convert |
| 86 | */ |
| 87 | function testAutoConvertBalance(uint8 targetAcc) public { |
| 88 | address target = getAccount(targetAcc); |
| 89 | |
| 90 | uint256 balanceBefore = ousd.balanceOf(target); |
| 91 | // slither-disable-next-line unused-return |
| 92 | ousd._isNonRebasingAccountEchidna(target); |
| 93 | uint256 balanceAfter = ousd.balanceOf(target); |
| 94 | |
| 95 | assert(balanceAfter == balanceBefore); |
| 96 | } |
| 97 | |
| 98 | /** |
| 99 | * @notice The `balanceOf` function should never revert |
| 100 | * @param targetAcc Account to check balance of |
| 101 | */ |
| 102 | function testBalanceOfShouldNotRevert(uint8 targetAcc) public { |
| 103 | address target = getAccount(targetAcc); |
| 104 | |
| 105 | // slither-disable-next-line unused-return |
| 106 | try ousd.balanceOf(target) { |
| 107 | assert(true); |
| 108 | } catch { |
| 109 | assert(false); |
| 110 | } |
| 111 | } |
| 112 | } |
| 113 |
96.0%
contracts/contracts/echidna/EchidnaTestApproval.sol
Lines covered: 30 / 31 (96.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | import "./EchidnaTestMintBurn.sol"; |
| 5 | import "./Debugger.sol"; |
| 6 | |
| 7 | /** |
| 8 | * @title Mixin for testing approval related functions |
| 9 | * @author Rappie |
| 10 | */ |
| 11 | contract EchidnaTestApproval is EchidnaTestMintBurn { |
| 12 | /** |
| 13 | * @notice Performing `transferFrom` with an amount inside the allowance should not revert |
| 14 | * @param authorizedAcc The account that is authorized to transfer |
| 15 | * @param fromAcc The account that is transferring |
| 16 | * @param toAcc The account that is receiving |
| 17 | * @param amount The amount to transfer |
| 18 | */ |
| 19 | function testTransferFromShouldNotRevert( |
| 20 | uint8 authorizedAcc, |
| 21 | uint8 fromAcc, |
| 22 | uint8 toAcc, |
| 23 | uint256 amount |
| 24 | ) public { |
| 25 | address authorized = getAccount(authorizedAcc); |
| 26 | address from = getAccount(fromAcc); |
| 27 | address to = getAccount(toAcc); |
| 28 | |
| 29 | require(amount <= ousd.balanceOf(from)); |
| 30 | require(amount <= ousd.allowance(from, authorized)); |
| 31 | |
| 32 | hevm.prank(authorized); |
| 33 | // slither-disable-next-line unchecked-transfer |
| 34 | try ousd.transferFrom(from, to, amount) { |
| 35 | // pass |
| 36 | } catch { |
| 37 | assert(false); |
| 38 | } |
| 39 | } |
| 40 | |
| 41 | /** |
| 42 | * @notice Performing `transferFrom` with an amount outside the allowance should revert |
| 43 | * @param authorizedAcc The account that is authorized to transfer |
| 44 | * @param fromAcc The account that is transferring |
| 45 | * @param toAcc The account that is receiving |
| 46 | * @param amount The amount to transfer |
| 47 | */ |
| 48 | function testTransferFromShouldRevert( |
| 49 | uint8 authorizedAcc, |
| 50 | uint8 fromAcc, |
| 51 | uint8 toAcc, |
| 52 | uint256 amount |
| 53 | ) public { |
| 54 | address authorized = getAccount(authorizedAcc); |
| 55 | address from = getAccount(fromAcc); |
| 56 | address to = getAccount(toAcc); |
| 57 | |
| 58 | require(amount > 0); |
| 59 | require( |
| 60 | !(amount <= ousd.balanceOf(from) && |
| 61 | amount <= ousd.allowance(from, authorized)) |
| 62 | ); |
| 63 | |
| 64 | hevm.prank(authorized); |
| 65 | // slither-disable-next-line unchecked-transfer |
| 66 | try ousd.transferFrom(from, to, amount) { |
| 67 | assert(false); |
| 68 | } catch { |
| 69 | // pass |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | /** |
| 74 | * @notice Approving an amount should update the allowance and overwrite any previous allowance |
| 75 | * @param ownerAcc The account that is approving |
| 76 | * @param spenderAcc The account that is being approved |
| 77 | * @param amount The amount to approve |
| 78 | */ |
| 79 | function testApprove( |
| 80 | uint8 ownerAcc, |
| 81 | uint8 spenderAcc, |
| 82 | uint256 amount |
| 83 | ) public { |
| 84 | address owner = getAccount(ownerAcc); |
| 85 | address spender = getAccount(spenderAcc); |
| 86 | |
| 87 | approve(ownerAcc, spenderAcc, amount); |
| 88 | uint256 allowanceAfter1 = ousd.allowance(owner, spender); |
| 89 | |
| 90 | assert(allowanceAfter1 == amount); |
| 91 | |
| 92 | approve(ownerAcc, spenderAcc, amount / 2); |
| 93 | uint256 allowanceAfter2 = ousd.allowance(owner, spender); |
| 94 | |
| 95 | assert(allowanceAfter2 == amount / 2); |
| 96 | } |
| 97 | } |
| 98 |
95.0%
contracts/contracts/echidna/EchidnaTestMintBurn.sol
Lines covered: 43 / 45 (95.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | import "./EchidnaDebug.sol"; |
| 5 | import "./EchidnaTestAccounting.sol"; |
| 6 | |
| 7 | /** |
| 8 | * @title Mixin for testing Mint and Burn functions |
| 9 | * @author Rappie |
| 10 | */ |
| 11 | contract EchidnaTestMintBurn is EchidnaTestAccounting { |
| 12 | /** |
| 13 | * @notice Minting 0 tokens should not affect account balance |
| 14 | * @param targetAcc Account to mint to |
| 15 | */ |
| 16 | function testMintZeroBalance(uint8 targetAcc) public { |
| 17 | address target = getAccount(targetAcc); |
| 18 | |
| 19 | uint256 balanceBefore = ousd.balanceOf(target); |
| 20 | mint(targetAcc, 0); |
| 21 | uint256 balanceAfter = ousd.balanceOf(target); |
| 22 | |
| 23 | assert(balanceAfter == balanceBefore); |
| 24 | } |
| 25 | |
| 26 | /** |
| 27 | * @notice Burning 0 tokens should not affect account balance |
| 28 | * @param targetAcc Account to burn from |
| 29 | */ |
| 30 | function testBurnZeroBalance(uint8 targetAcc) public { |
| 31 | address target = getAccount(targetAcc); |
| 32 | |
| 33 | uint256 balanceBefore = ousd.balanceOf(target); |
| 34 | burn(targetAcc, 0); |
| 35 | uint256 balanceAfter = ousd.balanceOf(target); |
| 36 | |
| 37 | assert(balanceAfter == balanceBefore); |
| 38 | } |
| 39 | |
| 40 | /** |
| 41 | * @notice Minting tokens must increase the account balance by at least amount |
| 42 | * @param targetAcc Account to mint to |
| 43 | * @param amount Amount to mint |
| 44 | * @custom:error testMintBalance(uint8,uint256): failed!💥 |
| 45 | * Call sequence: |
| 46 | * changeSupply(1) |
| 47 | * testMintBalance(0,1) |
| 48 | * Event sequence: |
| 49 | * Debug(«balanceBefore», 0) |
| 50 | * Debug(«balanceAfter», 0) |
| 51 | */ |
| 52 | function testMintBalance(uint8 targetAcc, uint256 amount) |
| 53 | public |
| 54 | hasKnownIssue |
| 55 | hasKnownIssueWithinLimits |
| 56 | { |
| 57 | address target = getAccount(targetAcc); |
| 58 | |
| 59 | uint256 balanceBefore = ousd.balanceOf(target); |
| 60 | uint256 amountMinted = mint(targetAcc, amount); |
| 61 | uint256 balanceAfter = ousd.balanceOf(target); |
| 62 | |
| 63 | Debugger.log("amountMinted", amountMinted); |
| 64 | Debugger.log("balanceBefore", balanceBefore); |
| 65 | Debugger.log("balanceAfter", balanceAfter); |
| 66 | |
| 67 | assert(balanceAfter >= balanceBefore + amountMinted); |
| 68 | } |
| 69 | |
| 70 | /** |
| 71 | * @notice Burning tokens must decrease the account balance by at least amount |
| 72 | * @param targetAcc Account to burn from |
| 73 | * @param amount Amount to burn |
| 74 | * @custom:error testBurnBalance(uint8,uint256): failed!💥 |
| 75 | * Call sequence: |
| 76 | * changeSupply(1) |
| 77 | * mint(0,3) |
| 78 | * testBurnBalance(0,1) |
| 79 | * Event sequence: |
| 80 | * Debug(«balanceBefore», 2) |
| 81 | * Debug(«balanceAfter», 2) |
| 82 | */ |
| 83 | function testBurnBalance(uint8 targetAcc, uint256 amount) |
| 84 | public |
| 85 | hasKnownIssue |
| 86 | hasKnownIssueWithinLimits |
| 87 | { |
| 88 | address target = getAccount(targetAcc); |
| 89 | |
| 90 | uint256 balanceBefore = ousd.balanceOf(target); |
| 91 | burn(targetAcc, amount); |
| 92 | uint256 balanceAfter = ousd.balanceOf(target); |
| 93 | |
| 94 | Debugger.log("balanceBefore", balanceBefore); |
| 95 | Debugger.log("balanceAfter", balanceAfter); |
| 96 | |
| 97 | assert(balanceAfter <= balanceBefore - amount); |
| 98 | } |
| 99 | |
| 100 | /** |
| 101 | * @notice Minting tokens should not increase the account balance by less than rounding error above amount |
| 102 | * @param targetAcc Account to mint to |
| 103 | * @param amount Amount to mint |
| 104 | */ |
| 105 | function testMintBalanceRounding(uint8 targetAcc, uint256 amount) public { |
| 106 | address target = getAccount(targetAcc); |
| 107 | |
| 108 | uint256 balanceBefore = ousd.balanceOf(target); |
| 109 | uint256 amountMinted = mint(targetAcc, amount); |
| 110 | uint256 balanceAfter = ousd.balanceOf(target); |
| 111 | |
| 112 | int256 delta = int256(balanceAfter) - int256(balanceBefore); |
| 113 | |
| 114 | // delta == amount, if no error |
| 115 | // delta < amount, if too little is minted |
| 116 | // delta > amount, if too much is minted |
| 117 | int256 error = int256(amountMinted) - delta; |
| 118 | |
| 119 | assert(error >= 0); |
| 120 | assert(error <= int256(MINT_ROUNDING_ERROR)); |
| 121 | } |
| 122 | |
| 123 | /** |
| 124 | * @notice A burn of an account balance must result in a zero balance |
| 125 | * @param targetAcc Account to burn from |
| 126 | */ |
| 127 | function testBurnAllBalanceToZero(uint8 targetAcc) public hasKnownIssue { |
| 128 | address target = getAccount(targetAcc); |
| 129 | |
| 130 | burn(targetAcc, ousd.balanceOf(target)); |
| 131 | assert(ousd.balanceOf(target) == 0); |
| 132 | } |
| 133 | |
| 134 | /** |
| 135 | * @notice You should always be able to burn an account's balance |
| 136 | * @param targetAcc Account to burn from |
| 137 | */ |
| 138 | function testBurnAllBalanceShouldNotRevert(uint8 targetAcc) |
| 139 | public |
| 140 | hasKnownIssue |
| 141 | { |
| 142 | address target = getAccount(targetAcc); |
| 143 | uint256 balance = ousd.balanceOf(target); |
| 144 | |
| 145 | hevm.prank(ADDRESS_VAULT); |
| 146 | try ousd.burn(target, balance) { |
| 147 | assert(true); |
| 148 | } catch { |
| 149 | assert(false); |
| 150 | } |
| 151 | } |
| 152 | } |
| 153 |
95.0%
contracts/contracts/echidna/EchidnaTestSupply.sol
Lines covered: 39 / 41 (95.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | import "./EchidnaDebug.sol"; |
| 5 | import "./EchidnaTestTransfer.sol"; |
| 6 | |
| 7 | import { StableMath } from "../utils/StableMath.sol"; |
| 8 | |
| 9 | /** |
| 10 | * @title Mixin for testing supply related functions |
| 11 | * @author Rappie |
| 12 | */ |
| 13 | contract EchidnaTestSupply is EchidnaTestTransfer { |
| 14 | using StableMath for uint256; |
| 15 | |
| 16 | uint256 prevRebasingCreditsPerToken = type(uint256).max; |
| 17 | |
| 18 | /** |
| 19 | * @notice After a `changeSupply`, the total supply should exactly |
| 20 | * match the target total supply. (This is needed to ensure successive |
| 21 | * rebases are correct). |
| 22 | * @param supply New total supply |
| 23 | * @custom:error testChangeSupply(uint256): failed!💥 |
| 24 | * Call sequence: |
| 25 | * testChangeSupply(1044505275072865171609) |
| 26 | * Event sequence: |
| 27 | * TotalSupplyUpdatedHighres(1044505275072865171610, 1000000000000000000000000, 957391048054055578595) |
| 28 | */ |
| 29 | function testChangeSupply(uint256 supply) |
| 30 | public |
| 31 | hasKnownIssue |
| 32 | hasKnownIssueWithinLimits |
| 33 | { |
| 34 | hevm.prank(ADDRESS_VAULT); |
| 35 | ousd.changeSupply(supply); |
| 36 | |
| 37 | assert(ousd.totalSupply() == supply); |
| 38 | } |
| 39 | |
| 40 | /** |
| 41 | * @notice The total supply must not be less than the sum of account balances. |
| 42 | * (The difference will go into future rebases) |
| 43 | * @custom:error testTotalSupplyLessThanTotalBalance(): failed!💥 |
| 44 | * Call sequence: |
| 45 | * mint(0,1) |
| 46 | * changeSupply(1) |
| 47 | * optOut(64) |
| 48 | * transfer(0,64,1) |
| 49 | * testTotalSupplyLessThanTotalBalance() |
| 50 | * Event sequence: |
| 51 | * Debug(«totalSupply», 1000000000000000001000001) |
| 52 | * Debug(«totalBalance», 1000000000000000001000002) |
| 53 | */ |
| 54 | function testTotalSupplyLessThanTotalBalance() |
| 55 | public |
| 56 | hasKnownIssue |
| 57 | hasKnownIssueWithinLimits |
| 58 | { |
| 59 | uint256 totalSupply = ousd.totalSupply(); |
| 60 | uint256 totalBalance = getTotalBalance(); |
| 61 | |
| 62 | Debugger.log("totalSupply", totalSupply); |
| 63 | Debugger.log("totalBalance", totalBalance); |
| 64 | |
| 65 | assert(totalSupply >= totalBalance); |
| 66 | } |
| 67 | |
| 68 | /** |
| 69 | * @notice Non-rebasing supply should not be larger than total supply |
| 70 | * @custom:error testNonRebasingSupplyVsTotalSupply(): failed!💥 |
| 71 | * Call sequence: |
| 72 | * mint(0,2) |
| 73 | * changeSupply(3) |
| 74 | * burn(0,1) |
| 75 | * optOut(0) |
| 76 | * testNonRebasingSupplyVsTotalSupply() |
| 77 | */ |
| 78 | function testNonRebasingSupplyVsTotalSupply() public hasKnownIssue { |
| 79 | uint256 nonRebasingSupply = ousd.nonRebasingSupply(); |
| 80 | uint256 totalSupply = ousd.totalSupply(); |
| 81 | |
| 82 | assert(nonRebasingSupply <= totalSupply); |
| 83 | } |
| 84 | |
| 85 | /** |
| 86 | * @notice Global `rebasingCreditsPerToken` should never increase |
| 87 | * @custom:error testRebasingCreditsPerTokenNotIncreased(): failed!💥 |
| 88 | * Call sequence: |
| 89 | * testRebasingCreditsPerTokenNotIncreased() |
| 90 | * changeSupply(1) |
| 91 | * testRebasingCreditsPerTokenNotIncreased() |
| 92 | */ |
| 93 | function testRebasingCreditsPerTokenNotIncreased() public hasKnownIssue { |
| 94 | uint256 curRebasingCreditsPerToken = ousd |
| 95 | .rebasingCreditsPerTokenHighres(); |
| 96 | |
| 97 | Debugger.log( |
| 98 | "prevRebasingCreditsPerToken", |
| 99 | prevRebasingCreditsPerToken |
| 100 | ); |
| 101 | Debugger.log("curRebasingCreditsPerToken", curRebasingCreditsPerToken); |
| 102 | |
| 103 | assert(curRebasingCreditsPerToken <= prevRebasingCreditsPerToken); |
| 104 | |
| 105 | prevRebasingCreditsPerToken = curRebasingCreditsPerToken; |
| 106 | } |
| 107 | |
| 108 | /** |
| 109 | * @notice The rebasing credits per token ratio must greater than zero |
| 110 | */ |
| 111 | function testRebasingCreditsPerTokenAboveZero() public { |
| 112 | assert(ousd.rebasingCreditsPerTokenHighres() > 0); |
| 113 | } |
| 114 | |
| 115 | /** |
| 116 | * @notice The sum of all non-rebasing balances should not be larger than |
| 117 | * non-rebasing supply |
| 118 | * @custom:error testTotalNonRebasingSupplyLessThanTotalBalance(): failed!💥 |
| 119 | * Call sequence |
| 120 | * mint(0,2) |
| 121 | * changeSupply(1) |
| 122 | * optOut(0) |
| 123 | * burn(0,1) |
| 124 | * testTotalNonRebasingSupplyLessThanTotalBalance() |
| 125 | * Event sequence: |
| 126 | * Debug(«totalNonRebasingSupply», 500000000000000000000001) |
| 127 | * Debug(«totalNonRebasingBalance», 500000000000000000000002) |
| 128 | */ |
| 129 | function testTotalNonRebasingSupplyLessThanTotalBalance() |
| 130 | public |
| 131 | hasKnownIssue |
| 132 | hasKnownIssueWithinLimits |
| 133 | { |
| 134 | uint256 totalNonRebasingSupply = ousd.nonRebasingSupply(); |
| 135 | uint256 totalNonRebasingBalance = getTotalNonRebasingBalance(); |
| 136 | |
| 137 | Debugger.log("totalNonRebasingSupply", totalNonRebasingSupply); |
| 138 | Debugger.log("totalNonRebasingBalance", totalNonRebasingBalance); |
| 139 | |
| 140 | assert(totalNonRebasingSupply >= totalNonRebasingBalance); |
| 141 | } |
| 142 | |
| 143 | /** |
| 144 | * @notice An accounts credits / credits per token should not be larger it's balance |
| 145 | * @param targetAcc The account to check |
| 146 | */ |
| 147 | function testCreditsPerTokenVsBalance(uint8 targetAcc) public { |
| 148 | address target = getAccount(targetAcc); |
| 149 | |
| 150 | (uint256 credits, uint256 creditsPerToken, ) = ousd |
| 151 | .creditsBalanceOfHighres(target); |
| 152 | uint256 expectedBalance = credits.divPrecisely(creditsPerToken); |
| 153 | |
| 154 | uint256 balance = ousd.balanceOf(target); |
| 155 | |
| 156 | Debugger.log("credits", credits); |
| 157 | Debugger.log("creditsPerToken", creditsPerToken); |
| 158 | Debugger.log("expectedBalance", expectedBalance); |
| 159 | Debugger.log("balance", balance); |
| 160 | |
| 161 | assert(expectedBalance == balance); |
| 162 | } |
| 163 | } |
| 164 |
96.0%
contracts/contracts/echidna/EchidnaTestTransfer.sol
Lines covered: 103 / 107 (96.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | import "./EchidnaDebug.sol"; |
| 5 | import "./Debugger.sol"; |
| 6 | |
| 7 | /** |
| 8 | * @title Mixin for testing transfer related functions |
| 9 | * @author Rappie |
| 10 | */ |
| 11 | contract EchidnaTestTransfer is EchidnaDebug { |
| 12 | /** |
| 13 | * @notice The receiving account's balance after a transfer must not increase by |
| 14 | * less than the amount transferred |
| 15 | * @param fromAcc Account to transfer from |
| 16 | * @param toAcc Account to transfer to |
| 17 | * @param amount Amount to transfer |
| 18 | * @custom:error testTransferBalanceReceivedLess(uint8,uint8,uint256): failed!💥 |
| 19 | * Call sequence: |
| 20 | * changeSupply(1) |
| 21 | * mint(64,2) |
| 22 | * testTransferBalanceReceivedLess(64,0,1) |
| 23 | * Event sequence: |
| 24 | * Debug(«totalSupply», 1000000000000000000500002) |
| 25 | * Debug(«toBalBefore», 0) |
| 26 | * Debug(«toBalAfter», 0) |
| 27 | */ |
| 28 | function testTransferBalanceReceivedLess( |
| 29 | uint8 fromAcc, |
| 30 | uint8 toAcc, |
| 31 | uint256 amount |
| 32 | ) public hasKnownIssue hasKnownIssueWithinLimits { |
| 33 | address from = getAccount(fromAcc); |
| 34 | address to = getAccount(toAcc); |
| 35 | |
| 36 | require(from != to); |
| 37 | |
| 38 | uint256 toBalBefore = ousd.balanceOf(to); |
| 39 | transfer(fromAcc, toAcc, amount); |
| 40 | uint256 toBalAfter = ousd.balanceOf(to); |
| 41 | |
| 42 | Debugger.log("totalSupply", ousd.totalSupply()); |
| 43 | Debugger.log("toBalBefore", toBalBefore); |
| 44 | Debugger.log("toBalAfter", toBalAfter); |
| 45 | |
| 46 | assert(toBalAfter >= toBalBefore + amount); |
| 47 | } |
| 48 | |
| 49 | /** |
| 50 | * @notice The receiving account's balance after a transfer must not |
| 51 | * increase by more than the amount transferred |
| 52 | * @param fromAcc Account to transfer from |
| 53 | * @param toAcc Account to transfer to |
| 54 | * @param amount Amount to transfer |
| 55 | */ |
| 56 | function testTransferBalanceReceivedMore( |
| 57 | uint8 fromAcc, |
| 58 | uint8 toAcc, |
| 59 | uint256 amount |
| 60 | ) public { |
| 61 | address from = getAccount(fromAcc); |
| 62 | address to = getAccount(toAcc); |
| 63 | |
| 64 | require(from != to); |
| 65 | |
| 66 | uint256 toBalBefore = ousd.balanceOf(to); |
| 67 | transfer(fromAcc, toAcc, amount); |
| 68 | uint256 toBalAfter = ousd.balanceOf(to); |
| 69 | |
| 70 | Debugger.log("totalSupply", ousd.totalSupply()); |
| 71 | Debugger.log("toBalBefore", toBalBefore); |
| 72 | Debugger.log("toBalAfter", toBalAfter); |
| 73 | |
| 74 | assert(toBalAfter <= toBalBefore + amount); |
| 75 | } |
| 76 | |
| 77 | /** |
| 78 | * @notice The sending account's balance after a transfer must not |
| 79 | * decrease by less than the amount transferred |
| 80 | * @param fromAcc Account to transfer from |
| 81 | * @param toAcc Account to transfer to |
| 82 | * @param amount Amount to transfer |
| 83 | * @custom:error testTransferBalanceSentLess(uint8,uint8,uint256): failed!💥 |
| 84 | * Call sequence: |
| 85 | * mint(0,1) |
| 86 | * changeSupply(1) |
| 87 | * testTransferBalanceSentLess(0,64,1) |
| 88 | * Event sequence: |
| 89 | * Debug(«totalSupply», 1000000000000000000500001) |
| 90 | * Debug(«fromBalBefore», 1) |
| 91 | * Debug(«fromBalAfter», 1) |
| 92 | */ |
| 93 | function testTransferBalanceSentLess( |
| 94 | uint8 fromAcc, |
| 95 | uint8 toAcc, |
| 96 | uint256 amount |
| 97 | ) public hasKnownIssue hasKnownIssueWithinLimits { |
| 98 | address from = getAccount(fromAcc); |
| 99 | address to = getAccount(toAcc); |
| 100 | |
| 101 | require(from != to); |
| 102 | |
| 103 | uint256 fromBalBefore = ousd.balanceOf(from); |
| 104 | transfer(fromAcc, toAcc, amount); |
| 105 | uint256 fromBalAfter = ousd.balanceOf(from); |
| 106 | |
| 107 | Debugger.log("totalSupply", ousd.totalSupply()); |
| 108 | Debugger.log("fromBalBefore", fromBalBefore); |
| 109 | Debugger.log("fromBalAfter", fromBalAfter); |
| 110 | |
| 111 | assert(fromBalAfter <= fromBalBefore - amount); |
| 112 | } |
| 113 | |
| 114 | /** |
| 115 | * @notice The sending account's balance after a transfer must not |
| 116 | * decrease by more than the amount transferred |
| 117 | * @param fromAcc Account to transfer from |
| 118 | * @param toAcc Account to transfer to |
| 119 | * @param amount Amount to transfer |
| 120 | */ |
| 121 | function testTransferBalanceSentMore( |
| 122 | uint8 fromAcc, |
| 123 | uint8 toAcc, |
| 124 | uint256 amount |
| 125 | ) public { |
| 126 | address from = getAccount(fromAcc); |
| 127 | address to = getAccount(toAcc); |
| 128 | |
| 129 | require(from != to); |
| 130 | |
| 131 | uint256 fromBalBefore = ousd.balanceOf(from); |
| 132 | transfer(fromAcc, toAcc, amount); |
| 133 | uint256 fromBalAfter = ousd.balanceOf(from); |
| 134 | |
| 135 | Debugger.log("totalSupply", ousd.totalSupply()); |
| 136 | Debugger.log("fromBalBefore", fromBalBefore); |
| 137 | Debugger.log("fromBalAfter", fromBalAfter); |
| 138 | |
| 139 | assert(fromBalAfter >= fromBalBefore - amount); |
| 140 | } |
| 141 | |
| 142 | /** |
| 143 | * @notice The receiving account's balance after a transfer must not |
| 144 | * increase by less than the amount transferred (minus rounding error) |
| 145 | * @param fromAcc Account to transfer from |
| 146 | * @param toAcc Account to transfer to |
| 147 | * @param amount Amount to transfer |
| 148 | */ |
| 149 | function testTransferBalanceReceivedLessRounding( |
| 150 | uint8 fromAcc, |
| 151 | uint8 toAcc, |
| 152 | uint256 amount |
| 153 | ) public { |
| 154 | address from = getAccount(fromAcc); |
| 155 | address to = getAccount(toAcc); |
| 156 | |
| 157 | require(from != to); |
| 158 | |
| 159 | uint256 toBalBefore = ousd.balanceOf(to); |
| 160 | transfer(fromAcc, toAcc, amount); |
| 161 | uint256 toBalAfter = ousd.balanceOf(to); |
| 162 | |
| 163 | int256 toDelta = int256(toBalAfter) - int256(toBalBefore); |
| 164 | |
| 165 | // delta == amount, if no error |
| 166 | // delta < amount, if too little is sent |
| 167 | // delta > amount, if too much is sent |
| 168 | int256 error = int256(amount) - toDelta; |
| 169 | |
| 170 | Debugger.log("totalSupply", ousd.totalSupply()); |
| 171 | Debugger.log("toBalBefore", toBalBefore); |
| 172 | Debugger.log("toBalAfter", toBalAfter); |
| 173 | Debugger.log("toDelta", toDelta); |
| 174 | Debugger.log("error", error); |
| 175 | |
| 176 | assert(error >= 0); |
| 177 | assert(error <= int256(TRANSFER_ROUNDING_ERROR)); |
| 178 | } |
| 179 | |
| 180 | /** |
| 181 | * @notice The sending account's balance after a transfer must |
| 182 | * not decrease by less than the amount transferred (minus rounding error) |
| 183 | * @param fromAcc Account to transfer from |
| 184 | * @param toAcc Account to transfer to |
| 185 | * @param amount Amount to transfer |
| 186 | */ |
| 187 | function testTransferBalanceSentLessRounding( |
| 188 | uint8 fromAcc, |
| 189 | uint8 toAcc, |
| 190 | uint256 amount |
| 191 | ) public { |
| 192 | address from = getAccount(fromAcc); |
| 193 | address to = getAccount(toAcc); |
| 194 | |
| 195 | require(from != to); |
| 196 | |
| 197 | uint256 fromBalBefore = ousd.balanceOf(from); |
| 198 | transfer(fromAcc, toAcc, amount); |
| 199 | uint256 fromBalAfter = ousd.balanceOf(from); |
| 200 | |
| 201 | int256 fromDelta = int256(fromBalAfter) - int256(fromBalBefore); |
| 202 | |
| 203 | // delta == -amount, if no error |
| 204 | // delta < -amount, if too much is sent |
| 205 | // delta > -amount, if too little is sent |
| 206 | int256 error = int256(amount) + fromDelta; |
| 207 | |
| 208 | Debugger.log("totalSupply", ousd.totalSupply()); |
| 209 | Debugger.log("fromBalBefore", fromBalBefore); |
| 210 | Debugger.log("fromBalAfter", fromBalAfter); |
| 211 | Debugger.log("fromDelta", fromDelta); |
| 212 | Debugger.log("error", error); |
| 213 | |
| 214 | assert(error >= 0); |
| 215 | assert(error <= int256(TRANSFER_ROUNDING_ERROR)); |
| 216 | } |
| 217 | |
| 218 | /** |
| 219 | * @notice An account should always be able to successfully transfer |
| 220 | * an amount within its balance. |
| 221 | * @param fromAcc Account to transfer from |
| 222 | * @param toAcc Account to transfer to |
| 223 | * @param amount Amount to transfer |
| 224 | * @custom:error testTransferWithinBalanceDoesNotRevert(uint8,uint8,uint8): failed!💥 |
| 225 | * Call sequence: |
| 226 | * mint(0,1) |
| 227 | * changeSupply(3) |
| 228 | * optOut(0) |
| 229 | * testTransferWithinBalanceDoesNotRevert(0,128,2) |
| 230 | * optIn(0) |
| 231 | * testTransferWithinBalanceDoesNotRevert(128,0,1) |
| 232 | * Event sequence: |
| 233 | * error Revert Panic(17): SafeMath over-/under-flows |
| 234 | */ |
| 235 | function testTransferWithinBalanceDoesNotRevert( |
| 236 | uint8 fromAcc, |
| 237 | uint8 toAcc, |
| 238 | uint256 amount |
| 239 | ) public hasKnownIssue { |
| 240 | address from = getAccount(fromAcc); |
| 241 | address to = getAccount(toAcc); |
| 242 | |
| 243 | require(amount > 0); |
| 244 | amount = amount % ousd.balanceOf(from); |
| 245 | |
| 246 | Debugger.log("Total supply", ousd.totalSupply()); |
| 247 | |
| 248 | hevm.prank(from); |
| 249 | // slither-disable-next-line unchecked-transfer |
| 250 | try ousd.transfer(to, amount) { |
| 251 | assert(true); |
| 252 | } catch { |
| 253 | assert(false); |
| 254 | } |
| 255 | } |
| 256 | |
| 257 | /** |
| 258 | * @notice An account should never be able to successfully transfer |
| 259 | * an amount greater than their balance. |
| 260 | * @param fromAcc Account to transfer from |
| 261 | * @param toAcc Account to transfer to |
| 262 | * @param amount Amount to transfer |
| 263 | */ |
| 264 | function testTransferExceedingBalanceReverts( |
| 265 | uint8 fromAcc, |
| 266 | uint8 toAcc, |
| 267 | uint256 amount |
| 268 | ) public { |
| 269 | address from = getAccount(fromAcc); |
| 270 | address to = getAccount(toAcc); |
| 271 | |
| 272 | amount = ousd.balanceOf(from) + 1 + amount; |
| 273 | |
| 274 | hevm.prank(from); |
| 275 | // slither-disable-next-line unchecked-transfer |
| 276 | try ousd.transfer(to, amount) { |
| 277 | assert(false); |
| 278 | } catch { |
| 279 | assert(true); |
| 280 | } |
| 281 | } |
| 282 | |
| 283 | /** |
| 284 | * @notice A transfer to the same account should not change that account's balance |
| 285 | * @param targetAcc Account to transfer to |
| 286 | * @param amount Amount to transfer |
| 287 | */ |
| 288 | function testTransferSelf(uint8 targetAcc, uint256 amount) public { |
| 289 | address target = getAccount(targetAcc); |
| 290 | |
| 291 | uint256 balanceBefore = ousd.balanceOf(target); |
| 292 | transfer(targetAcc, targetAcc, amount); |
| 293 | uint256 balanceAfter = ousd.balanceOf(target); |
| 294 | |
| 295 | assert(balanceBefore == balanceAfter); |
| 296 | } |
| 297 | |
| 298 | /** |
| 299 | * @notice Transfers to the zero account revert |
| 300 | * @param fromAcc Account to transfer from |
| 301 | * @param amount Amount to transfer |
| 302 | */ |
| 303 | function testTransferToZeroAddress(uint8 fromAcc, uint256 amount) public { |
| 304 | address from = getAccount(fromAcc); |
| 305 | |
| 306 | hevm.prank(from); |
| 307 | // slither-disable-next-line unchecked-transfer |
| 308 | try ousd.transfer(address(0), amount) { |
| 309 | assert(false); |
| 310 | } catch { |
| 311 | assert(true); |
| 312 | } |
| 313 | } |
| 314 | } |
| 315 |
0.0%
contracts/contracts/echidna/IHevm.sol
Lines covered: 0 / 0 (0.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | // https://github.com/ethereum/hevm/blob/main/doc/src/controlling-the-unit-testing-environment.md#cheat-codes |
| 5 | |
| 6 | interface IHevm { |
| 7 | function warp(uint256 x) external; |
| 8 | |
| 9 | function roll(uint256 x) external; |
| 10 | |
| 11 | function store( |
| 12 | address c, |
| 13 | bytes32 loc, |
| 14 | bytes32 val |
| 15 | ) external; |
| 16 | |
| 17 | function load(address c, bytes32 loc) external returns (bytes32 val); |
| 18 | |
| 19 | function sign(uint256 sk, bytes32 digest) |
| 20 | external |
| 21 | returns ( |
| 22 | uint8 v, |
| 23 | bytes32 r, |
| 24 | bytes32 s |
| 25 | ); |
| 26 | |
| 27 | function addr(uint256 sk) external returns (address addr); |
| 28 | |
| 29 | function ffi(string[] calldata) external returns (bytes memory); |
| 30 | |
| 31 | function prank(address sender) external; |
| 32 | } |
| 33 |
100.0%
contracts/contracts/echidna/OUSDEchidna.sol
Lines covered: 7 / 7 (100.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | import "../token/OUSD.sol"; |
| 5 | |
| 6 | contract OUSDEchidna is OUSD { |
| 7 | constructor() OUSD() { |
| 8 | // The Yield Delegation rewrite (#2298) left Governable without a |
| 9 | // constructor, so the governor slot is zero and the harness could |
| 10 | // never call initialize() (onlyGovernor). Make the deploying harness |
| 11 | // the governor so the suite is runnable again. |
| 12 | _setGovernor(msg.sender); |
| 13 | } |
| 14 | |
| 15 | function _isNonRebasingAccountEchidna(address _account) |
| 16 | public |
| 17 | returns (bool) |
| 18 | { |
| 19 | _autoMigrate(_account); |
| 20 | return alternativeCreditsPerToken[_account] > 0; |
| 21 | } |
| 22 | } |
| 23 |
41.0%
contracts/contracts/governance/Governable.sol
Lines covered: 10 / 24 (41.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | /** |
| 5 | * @title Base for contracts that are managed by the Origin Protocol's Governor. |
| 6 | * @dev Copy of the openzeppelin Ownable.sol contract with nomenclature change |
| 7 | * from owner to governor and renounce methods removed. Does not use |
| 8 | * Context.sol like Ownable.sol does for simplification. |
| 9 | * @author Origin Protocol Inc |
| 10 | */ |
| 11 | abstract contract Governable { |
| 12 | // Storage position of the owner and pendingOwner of the contract |
| 13 | // keccak256("OUSD.governor"); |
| 14 | bytes32 private constant governorPosition = |
| 15 | 0x7bea13895fa79d2831e0a9e28edede30099005a50d652d8957cf8a607ee6ca4a; |
| 16 | |
| 17 | // keccak256("OUSD.pending.governor"); |
| 18 | bytes32 private constant pendingGovernorPosition = |
| 19 | 0x44c4d30b2eaad5130ad70c3ba6972730566f3e6359ab83e800d905c61b1c51db; |
| 20 | |
| 21 | // keccak256("OUSD.reentry.status"); |
| 22 | bytes32 private constant reentryStatusPosition = |
| 23 | 0x53bf423e48ed90e97d02ab0ebab13b2a235a6bfbe9c321847d5c175333ac4535; |
| 24 | |
| 25 | // See OpenZeppelin ReentrancyGuard implementation |
| 26 | uint256 constant _NOT_ENTERED = 1; |
| 27 | uint256 constant _ENTERED = 2; |
| 28 | |
| 29 | event PendingGovernorshipTransfer( |
| 30 | address indexed previousGovernor, |
| 31 | address indexed newGovernor |
| 32 | ); |
| 33 | |
| 34 | event GovernorshipTransferred( |
| 35 | address indexed previousGovernor, |
| 36 | address indexed newGovernor |
| 37 | ); |
| 38 | |
| 39 | /** |
| 40 | * @notice Returns the address of the current Governor. |
| 41 | */ |
| 42 | function governor() public view returns (address) { |
| 43 | return _governor(); |
| 44 | } |
| 45 | |
| 46 | /** |
| 47 | * @dev Returns the address of the current Governor. |
| 48 | */ |
| 49 | function _governor() internal view returns (address governorOut) { |
| 50 | bytes32 position = governorPosition; |
| 51 | // solhint-disable-next-line no-inline-assembly |
| 52 | assembly { |
| 53 | governorOut := sload(position) |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | /** |
| 58 | * @dev Returns the address of the pending Governor. |
| 59 | */ |
| 60 | function _pendingGovernor() |
| 61 | internal |
| 62 | view |
| 63 | returns (address pendingGovernor) |
| 64 | { |
| 65 | bytes32 position = pendingGovernorPosition; |
| 66 | // solhint-disable-next-line no-inline-assembly |
| 67 | assembly { |
| 68 | pendingGovernor := sload(position) |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | /** |
| 73 | * @dev Throws if called by any account other than the Governor. |
| 74 | */ |
| 75 | modifier onlyGovernor() { |
| 76 | require(isGovernor(), "Caller is not the Governor"); |
| 77 | _; |
| 78 | } |
| 79 | |
| 80 | /** |
| 81 | * @notice Returns true if the caller is the current Governor. |
| 82 | */ |
| 83 | function isGovernor() public view returns (bool) { |
| 84 | return msg.sender == _governor(); |
| 85 | } |
| 86 | |
| 87 | function _setGovernor(address newGovernor) internal { |
| 88 | emit GovernorshipTransferred(_governor(), newGovernor); |
| 89 | |
| 90 | bytes32 position = governorPosition; |
| 91 | // solhint-disable-next-line no-inline-assembly |
| 92 | assembly { |
| 93 | sstore(position, newGovernor) |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | /** |
| 98 | * @dev Prevents a contract from calling itself, directly or indirectly. |
| 99 | * Calling a `nonReentrant` function from another `nonReentrant` |
| 100 | * function is not supported. It is possible to prevent this from happening |
| 101 | * by making the `nonReentrant` function external, and make it call a |
| 102 | * `private` function that does the actual work. |
| 103 | */ |
| 104 | modifier nonReentrant() { |
| 105 | bytes32 position = reentryStatusPosition; |
| 106 | uint256 _reentry_status; |
| 107 | // solhint-disable-next-line no-inline-assembly |
| 108 | assembly { |
| 109 | _reentry_status := sload(position) |
| 110 | } |
| 111 | |
| 112 | // On the first call to nonReentrant, _notEntered will be true |
| 113 | require(_reentry_status != _ENTERED, "Reentrant call"); |
| 114 | |
| 115 | // Any calls to nonReentrant after this point will fail |
| 116 | // solhint-disable-next-line no-inline-assembly |
| 117 | assembly { |
| 118 | sstore(position, _ENTERED) |
| 119 | } |
| 120 | |
| 121 | _; |
| 122 | |
| 123 | // By storing the original value once again, a refund is triggered (see |
| 124 | // https://eips.ethereum.org/EIPS/eip-2200) |
| 125 | // solhint-disable-next-line no-inline-assembly |
| 126 | assembly { |
| 127 | sstore(position, _NOT_ENTERED) |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | function _setPendingGovernor(address newGovernor) internal { |
| 132 | bytes32 position = pendingGovernorPosition; |
| 133 | // solhint-disable-next-line no-inline-assembly |
| 134 | assembly { |
| 135 | sstore(position, newGovernor) |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | /** |
| 140 | * @notice Transfers Governance of the contract to a new account (`newGovernor`). |
| 141 | * Can only be called by the current Governor. Must be claimed for this to complete |
| 142 | * @param _newGovernor Address of the new Governor |
| 143 | */ |
| 144 | function transferGovernance(address _newGovernor) external onlyGovernor { |
| 145 | _setPendingGovernor(_newGovernor); |
| 146 | emit PendingGovernorshipTransfer(_governor(), _newGovernor); |
| 147 | } |
| 148 | |
| 149 | /** |
| 150 | * @notice Claim Governance of the contract to a new account (`newGovernor`). |
| 151 | * Can only be called by the new Governor. |
| 152 | */ |
| 153 | function claimGovernance() external { |
| 154 | require( |
| 155 | msg.sender == _pendingGovernor(), |
| 156 | "Only the pending Governor can complete the claim" |
| 157 | ); |
| 158 | _changeGovernor(msg.sender); |
| 159 | } |
| 160 | |
| 161 | /** |
| 162 | * @dev Change Governance of the contract to a new account (`newGovernor`). |
| 163 | * @param _newGovernor Address of the new Governor |
| 164 | */ |
| 165 | function _changeGovernor(address _newGovernor) internal { |
| 166 | require(_newGovernor != address(0), "New Governor is address(0)"); |
| 167 | _setGovernor(_newGovernor); |
| 168 | } |
| 169 | } |
| 170 |
0.0%
contracts/contracts/interfaces/IBasicToken.sol
Lines covered: 0 / 0 (0.0%)
| 1 | // SPDX-License-Identifier: MIT |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | interface IBasicToken { |
| 5 | function symbol() external view returns (string memory); |
| 6 | |
| 7 | function decimals() external view returns (uint8); |
| 8 | } |
| 9 |
0.0%
contracts/contracts/interfaces/IStrategy.sol
Lines covered: 0 / 0 (0.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | /** |
| 5 | * @title Platform interface to integrate with lending platform like Compound, AAVE etc. |
| 6 | */ |
| 7 | interface IStrategy { |
| 8 | /** |
| 9 | * @dev Deposit the given asset to platform |
| 10 | * @param _asset asset address |
| 11 | * @param _amount Amount to deposit |
| 12 | */ |
| 13 | function deposit(address _asset, uint256 _amount) external; |
| 14 | |
| 15 | /** |
| 16 | * @dev Deposit the entire balance of all supported assets in the Strategy |
| 17 | * to the platform |
| 18 | */ |
| 19 | function depositAll() external; |
| 20 | |
| 21 | /** |
| 22 | * @dev Withdraw given asset from Lending platform |
| 23 | */ |
| 24 | function withdraw( |
| 25 | address _recipient, |
| 26 | address _asset, |
| 27 | uint256 _amount |
| 28 | ) external; |
| 29 | |
| 30 | /** |
| 31 | * @dev Liquidate all assets in strategy and return them to Vault. |
| 32 | */ |
| 33 | function withdrawAll() external; |
| 34 | |
| 35 | /** |
| 36 | * @dev Returns the current balance of the given asset. |
| 37 | */ |
| 38 | function checkBalance(address _asset) |
| 39 | external |
| 40 | view |
| 41 | returns (uint256 balance); |
| 42 | |
| 43 | /** |
| 44 | * @dev Returns bool indicating whether strategy supports asset. |
| 45 | */ |
| 46 | function supportsAsset(address _asset) external view returns (bool); |
| 47 | |
| 48 | /** |
| 49 | * @dev Collect reward tokens from the Strategy. |
| 50 | */ |
| 51 | function collectRewardTokens() external; |
| 52 | |
| 53 | /** |
| 54 | * @dev The address array of the reward tokens for the Strategy. |
| 55 | */ |
| 56 | function getRewardTokenAddresses() external view returns (address[] memory); |
| 57 | |
| 58 | function harvesterAddress() external view returns (address); |
| 59 | |
| 60 | function transferToken(address token, uint256 amount) external; |
| 61 | |
| 62 | function setRewardTokenAddresses(address[] calldata _rewardTokenAddresses) |
| 63 | external; |
| 64 | } |
| 65 |
0.0%
contracts/contracts/interfaces/IVault.sol
Lines covered: 0 / 0 (0.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | import { VaultStorage } from "../vault/VaultStorage.sol"; |
| 5 | |
| 6 | interface IVault { |
| 7 | // slither-disable-start constable-states |
| 8 | |
| 9 | event AssetAllocated(address _asset, address _strategy, uint256 _amount); |
| 10 | event StrategyApproved(address _addr); |
| 11 | event StrategyRemoved(address _addr); |
| 12 | event Mint(address _addr, uint256 _value); |
| 13 | event Redeem(address _addr, uint256 _value); |
| 14 | event CapitalPaused(); |
| 15 | event CapitalUnpaused(); |
| 16 | event DefaultStrategyUpdated(address _strategy); |
| 17 | event RebasePaused(); |
| 18 | event RebaseUnpaused(); |
| 19 | event VaultBufferUpdated(uint256 _vaultBuffer); |
| 20 | event AllocateThresholdUpdated(uint256 _threshold); |
| 21 | event StrategistUpdated(address _address); |
| 22 | event MaxSupplyDiffChanged(uint256 maxSupplyDiff); |
| 23 | event YieldDistribution(address _to, uint256 _yield, uint256 _fee); |
| 24 | event TrusteeFeeBpsChanged(uint256 _basis); |
| 25 | event TrusteeAddressChanged(address _address); |
| 26 | event StrategyAddedToMintWhitelist(address indexed strategy); |
| 27 | event StrategyRemovedFromMintWhitelist(address indexed strategy); |
| 28 | event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond); |
| 29 | event DripDurationChanged(uint256 dripDuration); |
| 30 | event WithdrawalRequested( |
| 31 | address indexed _withdrawer, |
| 32 | uint256 indexed _requestId, |
| 33 | uint256 _amount, |
| 34 | uint256 _queued |
| 35 | ); |
| 36 | event WithdrawalClaimed( |
| 37 | address indexed _withdrawer, |
| 38 | uint256 indexed _requestId, |
| 39 | uint256 _amount |
| 40 | ); |
| 41 | event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable); |
| 42 | event WithdrawalClaimDelayUpdated(uint256 _newDelay); |
| 43 | |
| 44 | // Governable.sol |
| 45 | function transferGovernance(address _newGovernor) external; |
| 46 | |
| 47 | function claimGovernance() external; |
| 48 | |
| 49 | function governor() external view returns (address); |
| 50 | |
| 51 | // VaultAdmin.sol |
| 52 | function setVaultBuffer(uint256 _vaultBuffer) external; |
| 53 | |
| 54 | function vaultBuffer() external view returns (uint256); |
| 55 | |
| 56 | function setAutoAllocateThreshold(uint256 _threshold) external; |
| 57 | |
| 58 | function autoAllocateThreshold() external view returns (uint256); |
| 59 | |
| 60 | function setStrategistAddr(address _address) external; |
| 61 | |
| 62 | function strategistAddr() external view returns (address); |
| 63 | |
| 64 | function setOperatorAddr(address _operator) external; |
| 65 | |
| 66 | function operatorAddr() external view returns (address); |
| 67 | |
| 68 | function setMaxSupplyDiff(uint256 _maxSupplyDiff) external; |
| 69 | |
| 70 | function maxSupplyDiff() external view returns (uint256); |
| 71 | |
| 72 | function setTrusteeAddress(address _address) external; |
| 73 | |
| 74 | function trusteeAddress() external view returns (address); |
| 75 | |
| 76 | function setTrusteeFeeBps(uint256 _basis) external; |
| 77 | |
| 78 | function trusteeFeeBps() external view returns (uint256); |
| 79 | |
| 80 | function approveStrategy(address _addr) external; |
| 81 | |
| 82 | function removeStrategy(address _addr) external; |
| 83 | |
| 84 | function setDefaultStrategy(address _strategy) external; |
| 85 | |
| 86 | function defaultStrategy() external view returns (address); |
| 87 | |
| 88 | function pauseRebase() external; |
| 89 | |
| 90 | function unpauseRebase() external; |
| 91 | |
| 92 | function rebasePaused() external view returns (bool); |
| 93 | |
| 94 | function pauseCapital() external; |
| 95 | |
| 96 | function unpauseCapital() external; |
| 97 | |
| 98 | function capitalPaused() external view returns (bool); |
| 99 | |
| 100 | function transferToken(address _asset, uint256 _amount) external; |
| 101 | |
| 102 | function withdrawAllFromStrategy(address _strategyAddr) external; |
| 103 | |
| 104 | function withdrawAllFromStrategies() external; |
| 105 | |
| 106 | function withdrawFromStrategy( |
| 107 | address _strategyFromAddress, |
| 108 | address[] calldata _assets, |
| 109 | uint256[] calldata _amounts |
| 110 | ) external; |
| 111 | |
| 112 | function depositToStrategy( |
| 113 | address _strategyToAddress, |
| 114 | address[] calldata _assets, |
| 115 | uint256[] calldata _amounts |
| 116 | ) external; |
| 117 | |
| 118 | // VaultCore.sol |
| 119 | function mint(uint256 _amount) external; |
| 120 | |
| 121 | function mintForStrategy(uint256 _amount) external; |
| 122 | |
| 123 | function burnForStrategy(uint256 _amount) external; |
| 124 | |
| 125 | function allocate() external; |
| 126 | |
| 127 | function rebase() external; |
| 128 | |
| 129 | function totalValue() external view returns (uint256 value); |
| 130 | |
| 131 | function checkBalance(address _asset) external view returns (uint256); |
| 132 | |
| 133 | function getAssetCount() external view returns (uint256); |
| 134 | |
| 135 | function getAllAssets() external view returns (address[] memory); |
| 136 | |
| 137 | function getStrategyCount() external view returns (uint256); |
| 138 | |
| 139 | function getAllStrategies() external view returns (address[] memory); |
| 140 | |
| 141 | function strategies(address _addr) |
| 142 | external |
| 143 | view |
| 144 | returns (VaultStorage.Strategy memory); |
| 145 | |
| 146 | /// @notice Deprecated: use `asset()` instead. |
| 147 | function isSupportedAsset(address _asset) external view returns (bool); |
| 148 | |
| 149 | function asset() external view returns (address); |
| 150 | |
| 151 | function oToken() external view returns (address); |
| 152 | |
| 153 | function initialize(address) external; |
| 154 | |
| 155 | function addWithdrawalQueueLiquidity() external; |
| 156 | |
| 157 | function requestWithdrawal(uint256 _amount) |
| 158 | external |
| 159 | returns (uint256 requestId, uint256 queued); |
| 160 | |
| 161 | function claimWithdrawal(uint256 requestId) |
| 162 | external |
| 163 | returns (uint256 amount); |
| 164 | |
| 165 | function claimWithdrawals(uint256[] memory requestIds) |
| 166 | external |
| 167 | returns (uint256[] memory amounts, uint256 totalAmount); |
| 168 | |
| 169 | function withdrawalQueueMetadata() |
| 170 | external |
| 171 | view |
| 172 | returns (VaultStorage.WithdrawalQueueMetadata memory); |
| 173 | |
| 174 | function withdrawalRequests(uint256 requestId) |
| 175 | external |
| 176 | view |
| 177 | returns (VaultStorage.WithdrawalRequest memory); |
| 178 | |
| 179 | function addStrategyToMintWhitelist(address strategyAddr) external; |
| 180 | |
| 181 | function removeStrategyFromMintWhitelist(address strategyAddr) external; |
| 182 | |
| 183 | function isMintWhitelistedStrategy(address strategyAddr) |
| 184 | external |
| 185 | view |
| 186 | returns (bool); |
| 187 | |
| 188 | function withdrawalClaimDelay() external view returns (uint256); |
| 189 | |
| 190 | function setWithdrawalClaimDelay(uint256 newDelay) external; |
| 191 | |
| 192 | function lastRebase() external view returns (uint64); |
| 193 | |
| 194 | function dripDuration() external view returns (uint64); |
| 195 | |
| 196 | function setDripDuration(uint256 _dripDuration) external; |
| 197 | |
| 198 | function rebasePerSecondMax() external view returns (uint64); |
| 199 | |
| 200 | function setRebaseRateMax(uint256 yearlyApr) external; |
| 201 | |
| 202 | function rebasePerSecondTarget() external view returns (uint64); |
| 203 | |
| 204 | function previewYield() external view returns (uint256 yield); |
| 205 | |
| 206 | // slither-disable-end constable-states |
| 207 | } |
| 208 |
63.0%
contracts/contracts/token/OUSD.sol
Lines covered: 181 / 284 (63.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | /** |
| 5 | * @title OUSD Token Contract |
| 6 | * @dev ERC20 compatible contract for OUSD |
| 7 | * @dev Implements an elastic supply |
| 8 | * @author Origin Protocol Inc |
| 9 | */ |
| 10 | import { IVault } from "../interfaces/IVault.sol"; |
| 11 | import { Governable } from "../governance/Governable.sol"; |
| 12 | import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; |
| 13 | |
| 14 | contract OUSD is Governable { |
| 15 | using SafeCast for int256; |
| 16 | using SafeCast for uint256; |
| 17 | |
| 18 | /// @dev Event triggered when the supply changes |
| 19 | /// @param totalSupply Updated token total supply |
| 20 | /// @param rebasingCredits Updated token rebasing credits |
| 21 | /// @param rebasingCreditsPerToken Updated token rebasing credits per token |
| 22 | event TotalSupplyUpdatedHighres( |
| 23 | uint256 totalSupply, |
| 24 | uint256 rebasingCredits, |
| 25 | uint256 rebasingCreditsPerToken |
| 26 | ); |
| 27 | /// @dev Event triggered when an account opts in for rebasing |
| 28 | /// @param account Address of the account |
| 29 | event AccountRebasingEnabled(address account); |
| 30 | /// @dev Event triggered when an account opts out of rebasing |
| 31 | /// @param account Address of the account |
| 32 | event AccountRebasingDisabled(address account); |
| 33 | /// @dev Emitted when `value` tokens are moved from one account `from` to |
| 34 | /// another `to`. |
| 35 | /// @param from Address of the account tokens are moved from |
| 36 | /// @param to Address of the account tokens are moved to |
| 37 | /// @param value Amount of tokens transferred |
| 38 | event Transfer(address indexed from, address indexed to, uint256 value); |
| 39 | /// @dev Emitted when the allowance of a `spender` for an `owner` is set by |
| 40 | /// a call to {approve}. `value` is the new allowance. |
| 41 | /// @param owner Address of the owner approving allowance |
| 42 | /// @param spender Address of the spender allowance is granted to |
| 43 | /// @param value Amount of tokens spender can transfer |
| 44 | event Approval( |
| 45 | address indexed owner, |
| 46 | address indexed spender, |
| 47 | uint256 value |
| 48 | ); |
| 49 | /// @dev Yield resulting from {changeSupply} that a `source` account would |
| 50 | /// receive is directed to `target` account. |
| 51 | /// @param source Address of the source forwarding the yield |
| 52 | /// @param target Address of the target receiving the yield |
| 53 | event YieldDelegated(address source, address target); |
| 54 | /// @dev Yield delegation from `source` account to the `target` account is |
| 55 | /// suspended. |
| 56 | /// @param source Address of the source suspending yield forwarding |
| 57 | /// @param target Address of the target no longer receiving yield from `source` |
| 58 | /// account |
| 59 | event YieldUndelegated(address source, address target); |
| 60 | |
| 61 | enum RebaseOptions { |
| 62 | NotSet, |
| 63 | StdNonRebasing, |
| 64 | StdRebasing, |
| 65 | YieldDelegationSource, |
| 66 | YieldDelegationTarget |
| 67 | } |
| 68 | |
| 69 | uint256[154] private _gap; // Slots to align with deployed contract |
| 70 | uint256 private constant MAX_SUPPLY = type(uint128).max; |
| 71 | /// @dev The amount of tokens in existence |
| 72 | uint256 public totalSupply; |
| 73 | mapping(address => mapping(address => uint256)) private allowances; |
| 74 | /// @dev The vault with privileges to execute {mint}, {burn} |
| 75 | /// and {changeSupply} |
| 76 | address public vaultAddress; |
| 77 | mapping(address => uint256) internal creditBalances; |
| 78 | // the 2 storage variables below need trailing underscores to not name collide with public functions |
| 79 | uint256 private rebasingCredits_; // Sum of all rebasing credits (creditBalances for rebasing accounts) |
| 80 | uint256 private rebasingCreditsPerToken_; |
| 81 | /// @dev The amount of tokens that are not rebasing - receiving yield |
| 82 | uint256 public nonRebasingSupply; |
| 83 | mapping(address => uint256) internal alternativeCreditsPerToken; |
| 84 | /// @dev A map of all addresses and their respective RebaseOptions |
| 85 | mapping(address => RebaseOptions) public rebaseState; |
| 86 | mapping(address => uint256) private __deprecated_isUpgraded; |
| 87 | /// @dev A map of addresses that have yields forwarded to. This is an |
| 88 | /// inverse mapping of {yieldFrom} |
| 89 | /// Key Account forwarding yield |
| 90 | /// Value Account receiving yield |
| 91 | mapping(address => address) public yieldTo; |
| 92 | /// @dev A map of addresses that are receiving the yield. This is an |
| 93 | /// inverse mapping of {yieldTo} |
| 94 | /// Key Account receiving yield |
| 95 | /// Value Account forwarding yield |
| 96 | mapping(address => address) public yieldFrom; |
| 97 | |
| 98 | uint256 private constant RESOLUTION_INCREASE = 1e9; |
| 99 | uint256[34] private __gap; // including below gap totals up to 200 |
| 100 | |
| 101 | /// @dev Verifies that the caller is the Governor or Strategist. |
| 102 | modifier onlyGovernorOrStrategist() { |
| 103 | require( |
| 104 | isGovernor() || msg.sender == IVault(vaultAddress).strategistAddr(), |
| 105 | "Caller is not the Strategist or Governor" |
| 106 | ); |
| 107 | _; |
| 108 | } |
| 109 | |
| 110 | /// @dev Initializes the contract and sets necessary variables. |
| 111 | /// @param _vaultAddress Address of the vault contract |
| 112 | /// @param _initialCreditsPerToken The starting rebasing credits per token. |
| 113 | function initialize(address _vaultAddress, uint256 _initialCreditsPerToken) |
| 114 | external |
| 115 | onlyGovernor |
| 116 | { |
| 117 | require(_vaultAddress != address(0), "Zero vault address"); |
| 118 | require(vaultAddress == address(0), "Already initialized"); |
| 119 | |
| 120 | rebasingCreditsPerToken_ = _initialCreditsPerToken; |
| 121 | vaultAddress = _vaultAddress; |
| 122 | } |
| 123 | |
| 124 | /// @dev Returns the symbol of the token, a shorter version |
| 125 | /// of the name. |
| 126 | function symbol() external pure virtual returns (string memory) { |
| 127 | return "OUSD"; |
| 128 | } |
| 129 | |
| 130 | /// @dev Returns the name of the token. |
| 131 | function name() external pure virtual returns (string memory) { |
| 132 | return "Origin Dollar"; |
| 133 | } |
| 134 | |
| 135 | /// @dev Returns the number of decimals used to get its user representation. |
| 136 | function decimals() external pure virtual returns (uint8) { |
| 137 | return 18; |
| 138 | } |
| 139 | |
| 140 | /** |
| 141 | * @dev Verifies that the caller is the Vault contract |
| 142 | */ |
| 143 | modifier onlyVault() { |
| 144 | require(vaultAddress == msg.sender, "Caller is not the Vault"); |
| 145 | _; |
| 146 | } |
| 147 | |
| 148 | /** |
| 149 | * @return High resolution rebasingCreditsPerToken |
| 150 | */ |
| 151 | function rebasingCreditsPerTokenHighres() external view returns (uint256) { |
| 152 | return rebasingCreditsPerToken_; |
| 153 | } |
| 154 | |
| 155 | /** |
| 156 | * @return Low resolution rebasingCreditsPerToken |
| 157 | */ |
| 158 | function rebasingCreditsPerToken() external view returns (uint256) { |
| 159 | return rebasingCreditsPerToken_ / RESOLUTION_INCREASE; |
| 160 | } |
| 161 | |
| 162 | /** |
| 163 | * @return High resolution total number of rebasing credits |
| 164 | */ |
| 165 | function rebasingCreditsHighres() external view returns (uint256) { |
| 166 | return rebasingCredits_; |
| 167 | } |
| 168 | |
| 169 | /** |
| 170 | * @return Low resolution total number of rebasing credits |
| 171 | */ |
| 172 | function rebasingCredits() external view returns (uint256) { |
| 173 | return rebasingCredits_ / RESOLUTION_INCREASE; |
| 174 | } |
| 175 | |
| 176 | /** |
| 177 | * @notice Gets the balance of the specified address. |
| 178 | * @param _account Address to query the balance of. |
| 179 | * @return A uint256 representing the amount of base units owned by the |
| 180 | * specified address. |
| 181 | */ |
| 182 | function balanceOf(address _account) public view returns (uint256) { |
| 183 | RebaseOptions state = rebaseState[_account]; |
| 184 | if (state == RebaseOptions.YieldDelegationSource) { |
| 185 | // Saves a slot read when transferring to or from a yield delegating source |
| 186 | // since we know creditBalances equals the balance. |
| 187 | return creditBalances[_account]; |
| 188 | } |
| 189 | uint256 baseBalance = (creditBalances[_account] * 1e18) / |
| 190 | _creditsPerToken(_account); |
| 191 | if (state == RebaseOptions.YieldDelegationTarget) { |
| 192 | // creditBalances of yieldFrom accounts equals token balances |
| 193 | return baseBalance - creditBalances[yieldFrom[_account]]; |
| 194 | } |
| 195 | return baseBalance; |
| 196 | } |
| 197 | |
| 198 | /** |
| 199 | * @notice Gets the credits balance of the specified address. |
| 200 | * @dev Backwards compatible with old low res credits per token. |
| 201 | * @param _account The address to query the balance of. |
| 202 | * @return (uint256, uint256) Credit balance and credits per token of the |
| 203 | * address |
| 204 | */ |
| 205 | function creditsBalanceOf(address _account) |
| 206 | external |
| 207 | view |
| 208 | returns (uint256, uint256) |
| 209 | { |
| 210 | uint256 cpt = _creditsPerToken(_account); |
| 211 | if (cpt == 1e27) { |
| 212 | // For a period before the resolution upgrade, we created all new |
| 213 | // contract accounts at high resolution. Since they are not changing |
| 214 | // as a result of this upgrade, we will return their true values |
| 215 | return (creditBalances[_account], cpt); |
| 216 | } else { |
| 217 | return ( |
| 218 | creditBalances[_account] / RESOLUTION_INCREASE, |
| 219 | cpt / RESOLUTION_INCREASE |
| 220 | ); |
| 221 | } |
| 222 | } |
| 223 | |
| 224 | /** |
| 225 | * @notice Gets the credits balance of the specified address. |
| 226 | * @param _account The address to query the balance of. |
| 227 | * @return (uint256, uint256, bool) Credit balance, credits per token of the |
| 228 | * address, and isUpgraded |
| 229 | */ |
| 230 | function creditsBalanceOfHighres(address _account) |
| 231 | external |
| 232 | view |
| 233 | returns ( |
| 234 | uint256, |
| 235 | uint256, |
| 236 | bool |
| 237 | ) |
| 238 | { |
| 239 | return ( |
| 240 | creditBalances[_account], |
| 241 | _creditsPerToken(_account), |
| 242 | true // all accounts have their resolution "upgraded" |
| 243 | ); |
| 244 | } |
| 245 | |
| 246 | // Backwards compatible view |
| 247 | function nonRebasingCreditsPerToken(address _account) |
| 248 | external |
| 249 | view |
| 250 | returns (uint256) |
| 251 | { |
| 252 | return alternativeCreditsPerToken[_account]; |
| 253 | } |
| 254 | |
| 255 | /** |
| 256 | * @notice Transfer tokens to a specified address. |
| 257 | * @param _to the address to transfer to. |
| 258 | * @param _value the amount to be transferred. |
| 259 | * @return true on success. |
| 260 | */ |
| 261 | function transfer(address _to, uint256 _value) external returns (bool) { |
| 262 | require(_to != address(0), "Transfer to zero address"); |
| 263 | |
| 264 | _executeTransfer(msg.sender, _to, _value); |
| 265 | |
| 266 | emit Transfer(msg.sender, _to, _value); |
| 267 | return true; |
| 268 | } |
| 269 | |
| 270 | /** |
| 271 | * @notice Transfer tokens from one address to another. |
| 272 | * @param _from The address you want to send tokens from. |
| 273 | * @param _to The address you want to transfer to. |
| 274 | * @param _value The amount of tokens to be transferred. |
| 275 | * @return true on success. |
| 276 | */ |
| 277 | function transferFrom( |
| 278 | address _from, |
| 279 | address _to, |
| 280 | uint256 _value |
| 281 | ) external returns (bool) { |
| 282 | require(_to != address(0), "Transfer to zero address"); |
| 283 | uint256 userAllowance = allowances[_from][msg.sender]; |
| 284 | require(_value <= userAllowance, "Allowance exceeded"); |
| 285 | |
| 286 | unchecked { |
| 287 | allowances[_from][msg.sender] = userAllowance - _value; |
| 288 | } |
| 289 | |
| 290 | _executeTransfer(_from, _to, _value); |
| 291 | |
| 292 | emit Transfer(_from, _to, _value); |
| 293 | return true; |
| 294 | } |
| 295 | |
| 296 | function _executeTransfer( |
| 297 | address _from, |
| 298 | address _to, |
| 299 | uint256 _value |
| 300 | ) internal { |
| 301 | ( |
| 302 | int256 fromRebasingCreditsDiff, |
| 303 | int256 fromNonRebasingSupplyDiff |
| 304 | ) = _adjustAccount(_from, -_value.toInt256()); |
| 305 | ( |
| 306 | int256 toRebasingCreditsDiff, |
| 307 | int256 toNonRebasingSupplyDiff |
| 308 | ) = _adjustAccount(_to, _value.toInt256()); |
| 309 | |
| 310 | _adjustGlobals( |
| 311 | fromRebasingCreditsDiff + toRebasingCreditsDiff, |
| 312 | fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff |
| 313 | ); |
| 314 | } |
| 315 | |
| 316 | function _adjustAccount(address _account, int256 _balanceChange) |
| 317 | internal |
| 318 | returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff) |
| 319 | { |
| 320 | RebaseOptions state = rebaseState[_account]; |
| 321 | int256 currentBalance = balanceOf(_account).toInt256(); |
| 322 | if (currentBalance + _balanceChange < 0) { |
| 323 | revert("Transfer amount exceeds balance"); |
| 324 | } |
| 325 | uint256 newBalance = (currentBalance + _balanceChange).toUint256(); |
| 326 | |
| 327 | if (state == RebaseOptions.YieldDelegationSource) { |
| 328 | address target = yieldTo[_account]; |
| 329 | uint256 targetOldBalance = balanceOf(target); |
| 330 | uint256 targetNewCredits = _balanceToRebasingCredits( |
| 331 | targetOldBalance + newBalance |
| 332 | ); |
| 333 | rebasingCreditsDiff = |
| 334 | targetNewCredits.toInt256() - |
| 335 | creditBalances[target].toInt256(); |
| 336 | |
| 337 | creditBalances[_account] = newBalance; |
| 338 | creditBalances[target] = targetNewCredits; |
| 339 | } else if (state == RebaseOptions.YieldDelegationTarget) { |
| 340 | uint256 newCredits = _balanceToRebasingCredits( |
| 341 | newBalance + creditBalances[yieldFrom[_account]] |
| 342 | ); |
| 343 | rebasingCreditsDiff = |
| 344 | newCredits.toInt256() - |
| 345 | creditBalances[_account].toInt256(); |
| 346 | creditBalances[_account] = newCredits; |
| 347 | } else { |
| 348 | _autoMigrate(_account); |
| 349 | uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[ |
| 350 | _account |
| 351 | ]; |
| 352 | if (alternativeCreditsPerTokenMem > 0) { |
| 353 | nonRebasingSupplyDiff = _balanceChange; |
| 354 | if (alternativeCreditsPerTokenMem != 1e18) { |
| 355 | alternativeCreditsPerToken[_account] = 1e18; |
| 356 | } |
| 357 | creditBalances[_account] = newBalance; |
| 358 | } else { |
| 359 | uint256 newCredits = _balanceToRebasingCredits(newBalance); |
| 360 | rebasingCreditsDiff = |
| 361 | newCredits.toInt256() - |
| 362 | creditBalances[_account].toInt256(); |
| 363 | creditBalances[_account] = newCredits; |
| 364 | } |
| 365 | } |
| 366 | } |
| 367 | |
| 368 | function _adjustGlobals( |
| 369 | int256 _rebasingCreditsDiff, |
| 370 | int256 _nonRebasingSupplyDiff |
| 371 | ) internal { |
| 372 | if (_rebasingCreditsDiff != 0) { |
| 373 | rebasingCredits_ = (rebasingCredits_.toInt256() + |
| 374 | _rebasingCreditsDiff).toUint256(); |
| 375 | } |
| 376 | if (_nonRebasingSupplyDiff != 0) { |
| 377 | nonRebasingSupply = (nonRebasingSupply.toInt256() + |
| 378 | _nonRebasingSupplyDiff).toUint256(); |
| 379 | } |
| 380 | } |
| 381 | |
| 382 | /** |
| 383 | * @notice Function to check the amount of tokens that _owner has allowed |
| 384 | * to `_spender`. |
| 385 | * @param _owner The address which owns the funds. |
| 386 | * @param _spender The address which will spend the funds. |
| 387 | * @return The number of tokens still available for the _spender. |
| 388 | */ |
| 389 | function allowance(address _owner, address _spender) |
| 390 | external |
| 391 | view |
| 392 | returns (uint256) |
| 393 | { |
| 394 | return allowances[_owner][_spender]; |
| 395 | } |
| 396 | |
| 397 | /** |
| 398 | * @notice Approve the passed address to spend the specified amount of |
| 399 | * tokens on behalf of msg.sender. |
| 400 | * @param _spender The address which will spend the funds. |
| 401 | * @param _value The amount of tokens to be spent. |
| 402 | * @return true on success. |
| 403 | */ |
| 404 | function approve(address _spender, uint256 _value) external returns (bool) { |
| 405 | allowances[msg.sender][_spender] = _value; |
| 406 | emit Approval(msg.sender, _spender, _value); |
| 407 | return true; |
| 408 | } |
| 409 | |
| 410 | /** |
| 411 | * @notice Creates `_amount` tokens and assigns them to `_account`, |
| 412 | * increasing the total supply. |
| 413 | */ |
| 414 | function mint(address _account, uint256 _amount) external onlyVault { |
| 415 | require(_account != address(0), "Mint to the zero address"); |
| 416 | |
| 417 | // Account |
| 418 | ( |
| 419 | int256 toRebasingCreditsDiff, |
| 420 | int256 toNonRebasingSupplyDiff |
| 421 | ) = _adjustAccount(_account, _amount.toInt256()); |
| 422 | // Globals |
| 423 | _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff); |
| 424 | totalSupply = totalSupply + _amount; |
| 425 | |
| 426 | require(totalSupply < MAX_SUPPLY, "Max supply"); |
| 427 | emit Transfer(address(0), _account, _amount); |
| 428 | } |
| 429 | |
| 430 | /** |
| 431 | * @notice Destroys `_amount` tokens from `_account`, |
| 432 | * reducing the total supply. |
| 433 | */ |
| 434 | function burn(address _account, uint256 _amount) external onlyVault { |
| 435 | require(_account != address(0), "Burn from the zero address"); |
| 436 | if (_amount == 0) { |
| 437 | return; |
| 438 | } |
| 439 | |
| 440 | // Account |
| 441 | ( |
| 442 | int256 toRebasingCreditsDiff, |
| 443 | int256 toNonRebasingSupplyDiff |
| 444 | ) = _adjustAccount(_account, -_amount.toInt256()); |
| 445 | // Globals |
| 446 | _adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff); |
| 447 | totalSupply = totalSupply - _amount; |
| 448 | |
| 449 | emit Transfer(_account, address(0), _amount); |
| 450 | } |
| 451 | |
| 452 | /** |
| 453 | * @dev Get the credits per token for an account. Returns a fixed amount |
| 454 | * if the account is non-rebasing. |
| 455 | * @param _account Address of the account. |
| 456 | */ |
| 457 | function _creditsPerToken(address _account) |
| 458 | internal |
| 459 | view |
| 460 | returns (uint256) |
| 461 | { |
| 462 | uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[ |
| 463 | _account |
| 464 | ]; |
| 465 | if (alternativeCreditsPerTokenMem != 0) { |
| 466 | return alternativeCreditsPerTokenMem; |
| 467 | } else { |
| 468 | return rebasingCreditsPerToken_; |
| 469 | } |
| 470 | } |
| 471 | |
| 472 | /** |
| 473 | * @dev Auto migrate contracts to be non rebasing, |
| 474 | * unless they have opted into yield. |
| 475 | * @param _account Address of the account. |
| 476 | */ |
| 477 | function _autoMigrate(address _account) internal { |
| 478 | uint256 codeLen = _account.code.length; |
| 479 | bool isEOA = (codeLen == 0) || |
| 480 | (codeLen == 23 && bytes3(_account.code) == 0xef0100); |
| 481 | // In previous code versions, contracts would not have had their |
| 482 | // rebaseState[_account] set to RebaseOptions.NonRebasing when migrated |
| 483 | // therefore we check the actual accounting used on the account as well. |
| 484 | if ( |
| 485 | (!isEOA) && |
| 486 | rebaseState[_account] == RebaseOptions.NotSet && |
| 487 | alternativeCreditsPerToken[_account] == 0 |
| 488 | ) { |
| 489 | _rebaseOptOut(_account); |
| 490 | } |
| 491 | } |
| 492 | |
| 493 | /** |
| 494 | * @dev Calculates credits from contract's global rebasingCreditsPerToken_, and |
| 495 | * also balance that corresponds to those credits. The latter is important |
| 496 | * when adjusting the contract's global nonRebasingSupply to circumvent any |
| 497 | * possible rounding errors. |
| 498 | * |
| 499 | * @param _balance Balance of the account. |
| 500 | */ |
| 501 | function _balanceToRebasingCredits(uint256 _balance) |
| 502 | internal |
| 503 | view |
| 504 | returns (uint256 rebasingCredits) |
| 505 | { |
| 506 | // Rounds up, because we need to ensure that accounts always have |
| 507 | // at least the balance that they should have. |
| 508 | // Note this should always be used on an absolute account value, |
| 509 | // not on a possibly negative diff, because then the rounding would be wrong. |
| 510 | return ((_balance) * rebasingCreditsPerToken_ + 1e18 - 1) / 1e18; |
| 511 | } |
| 512 | |
| 513 | /** |
| 514 | * @notice The calling account will start receiving yield after a successful call. |
| 515 | * @param _account Address of the account. |
| 516 | */ |
| 517 | function governanceRebaseOptIn(address _account) external onlyGovernor { |
| 518 | require(_account != address(0), "Zero address not allowed"); |
| 519 | _rebaseOptIn(_account); |
| 520 | } |
| 521 | |
| 522 | /** |
| 523 | * @notice The calling account will start receiving yield after a successful call. |
| 524 | */ |
| 525 | function rebaseOptIn() external { |
| 526 | _rebaseOptIn(msg.sender); |
| 527 | } |
| 528 | |
| 529 | function _rebaseOptIn(address _account) internal { |
| 530 | uint256 balance = balanceOf(_account); |
| 531 | |
| 532 | // prettier-ignore |
| 533 | require( |
| 534 | alternativeCreditsPerToken[_account] > 0 || |
| 535 | // Accounts may explicitly `rebaseOptIn` regardless of |
| 536 | // accounting if they have a 0 balance. |
| 537 | creditBalances[_account] == 0 |
| 538 | , |
| 539 | "Account must be non-rebasing" |
| 540 | ); |
| 541 | RebaseOptions state = rebaseState[_account]; |
| 542 | // prettier-ignore |
| 543 | require( |
| 544 | state == RebaseOptions.StdNonRebasing || |
| 545 | state == RebaseOptions.NotSet, |
| 546 | "Only standard non-rebasing accounts can opt in" |
| 547 | ); |
| 548 | |
| 549 | uint256 newCredits = _balanceToRebasingCredits(balance); |
| 550 | |
| 551 | // Account |
| 552 | rebaseState[_account] = RebaseOptions.StdRebasing; |
| 553 | alternativeCreditsPerToken[_account] = 0; |
| 554 | creditBalances[_account] = newCredits; |
| 555 | // Globals |
| 556 | _adjustGlobals(newCredits.toInt256(), -balance.toInt256()); |
| 557 | |
| 558 | emit AccountRebasingEnabled(_account); |
| 559 | } |
| 560 | |
| 561 | /** |
| 562 | * @notice The calling account will no longer receive yield |
| 563 | */ |
| 564 | function rebaseOptOut() external { |
| 565 | _rebaseOptOut(msg.sender); |
| 566 | } |
| 567 | |
| 568 | function _rebaseOptOut(address _account) internal { |
| 569 | require( |
| 570 | alternativeCreditsPerToken[_account] == 0, |
| 571 | "Account must be rebasing" |
| 572 | ); |
| 573 | RebaseOptions state = rebaseState[_account]; |
| 574 | require( |
| 575 | state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet, |
| 576 | "Only standard rebasing accounts can opt out" |
| 577 | ); |
| 578 | |
| 579 | uint256 oldCredits = creditBalances[_account]; |
| 580 | uint256 balance = balanceOf(_account); |
| 581 | |
| 582 | // Account |
| 583 | rebaseState[_account] = RebaseOptions.StdNonRebasing; |
| 584 | alternativeCreditsPerToken[_account] = 1e18; |
| 585 | creditBalances[_account] = balance; |
| 586 | // Globals |
| 587 | _adjustGlobals(-oldCredits.toInt256(), balance.toInt256()); |
| 588 | |
| 589 | emit AccountRebasingDisabled(_account); |
| 590 | } |
| 591 | |
| 592 | /** |
| 593 | * @notice Distribute yield to users. This changes the exchange rate |
| 594 | * between "credits" and OUSD tokens to change rebasing user's balances. |
| 595 | * @param _newTotalSupply New total supply of OUSD. |
| 596 | */ |
| 597 | function changeSupply(uint256 _newTotalSupply) external onlyVault { |
| 598 | require(totalSupply > 0, "Cannot increase 0 supply"); |
| 599 | |
| 600 | if (totalSupply == _newTotalSupply) { |
| 601 | emit TotalSupplyUpdatedHighres( |
| 602 | totalSupply, |
| 603 | rebasingCredits_, |
| 604 | rebasingCreditsPerToken_ |
| 605 | ); |
| 606 | return; |
| 607 | } |
| 608 | |
| 609 | totalSupply = _newTotalSupply > MAX_SUPPLY |
| 610 | ? MAX_SUPPLY |
| 611 | : _newTotalSupply; |
| 612 | |
| 613 | uint256 rebasingSupply = totalSupply - nonRebasingSupply; |
| 614 | // round up in the favour of the protocol |
| 615 | rebasingCreditsPerToken_ = |
| 616 | (rebasingCredits_ * 1e18 + rebasingSupply - 1) / |
| 617 | rebasingSupply; |
| 618 | |
| 619 | require(rebasingCreditsPerToken_ > 0, "Invalid change in supply"); |
| 620 | |
| 621 | emit TotalSupplyUpdatedHighres( |
| 622 | totalSupply, |
| 623 | rebasingCredits_, |
| 624 | rebasingCreditsPerToken_ |
| 625 | ); |
| 626 | } |
| 627 | |
| 628 | /* |
| 629 | * @notice Send the yield from one account to another account. |
| 630 | * Each account keeps its own balances. |
| 631 | */ |
| 632 | function delegateYield(address _from, address _to) |
| 633 | external |
| 634 | onlyGovernorOrStrategist |
| 635 | { |
| 636 | require(_from != address(0), "Zero from address not allowed"); |
| 637 | require(_to != address(0), "Zero to address not allowed"); |
| 638 | |
| 639 | require(_from != _to, "Cannot delegate to self"); |
| 640 | require( |
| 641 | yieldFrom[_to] == address(0) && |
| 642 | yieldTo[_to] == address(0) && |
| 643 | yieldFrom[_from] == address(0) && |
| 644 | yieldTo[_from] == address(0), |
| 645 | "Blocked by existing yield delegation" |
| 646 | ); |
| 647 | RebaseOptions stateFrom = rebaseState[_from]; |
| 648 | RebaseOptions stateTo = rebaseState[_to]; |
| 649 | |
| 650 | require( |
| 651 | stateFrom == RebaseOptions.NotSet || |
| 652 | stateFrom == RebaseOptions.StdNonRebasing || |
| 653 | stateFrom == RebaseOptions.StdRebasing, |
| 654 | "Invalid rebaseState from" |
| 655 | ); |
| 656 | |
| 657 | require( |
| 658 | stateTo == RebaseOptions.NotSet || |
| 659 | stateTo == RebaseOptions.StdNonRebasing || |
| 660 | stateTo == RebaseOptions.StdRebasing, |
| 661 | "Invalid rebaseState to" |
| 662 | ); |
| 663 | |
| 664 | if (alternativeCreditsPerToken[_from] == 0) { |
| 665 | _rebaseOptOut(_from); |
| 666 | } |
| 667 | if (alternativeCreditsPerToken[_to] > 0) { |
| 668 | _rebaseOptIn(_to); |
| 669 | } |
| 670 | |
| 671 | uint256 fromBalance = balanceOf(_from); |
| 672 | uint256 toBalance = balanceOf(_to); |
| 673 | uint256 oldToCredits = creditBalances[_to]; |
| 674 | uint256 newToCredits = _balanceToRebasingCredits( |
| 675 | fromBalance + toBalance |
| 676 | ); |
| 677 | |
| 678 | // Set up the bidirectional links |
| 679 | yieldTo[_from] = _to; |
| 680 | yieldFrom[_to] = _from; |
| 681 | |
| 682 | // Local |
| 683 | rebaseState[_from] = RebaseOptions.YieldDelegationSource; |
| 684 | alternativeCreditsPerToken[_from] = 1e18; |
| 685 | creditBalances[_from] = fromBalance; |
| 686 | rebaseState[_to] = RebaseOptions.YieldDelegationTarget; |
| 687 | creditBalances[_to] = newToCredits; |
| 688 | |
| 689 | // Global |
| 690 | int256 creditsChange = newToCredits.toInt256() - |
| 691 | oldToCredits.toInt256(); |
| 692 | _adjustGlobals(creditsChange, -(fromBalance).toInt256()); |
| 693 | emit YieldDelegated(_from, _to); |
| 694 | } |
| 695 | |
| 696 | /* |
| 697 | * @notice Stop sending the yield from one account to another account. |
| 698 | */ |
| 699 | function undelegateYield(address _from) external onlyGovernorOrStrategist { |
| 700 | // Require a delegation, which will also ensure a valid delegation |
| 701 | require(yieldTo[_from] != address(0), "Zero address not allowed"); |
| 702 | |
| 703 | address to = yieldTo[_from]; |
| 704 | uint256 fromBalance = balanceOf(_from); |
| 705 | uint256 toBalance = balanceOf(to); |
| 706 | uint256 oldToCredits = creditBalances[to]; |
| 707 | uint256 newToCredits = _balanceToRebasingCredits(toBalance); |
| 708 | |
| 709 | // Remove the bidirectional links |
| 710 | yieldFrom[to] = address(0); |
| 711 | yieldTo[_from] = address(0); |
| 712 | |
| 713 | // Local |
| 714 | rebaseState[_from] = RebaseOptions.StdNonRebasing; |
| 715 | // alternativeCreditsPerToken[from] already 1e18 from `delegateYield()` |
| 716 | creditBalances[_from] = fromBalance; |
| 717 | rebaseState[to] = RebaseOptions.StdRebasing; |
| 718 | // alternativeCreditsPerToken[to] already 0 from `delegateYield()` |
| 719 | creditBalances[to] = newToCredits; |
| 720 | |
| 721 | // Global |
| 722 | int256 creditsChange = newToCredits.toInt256() - |
| 723 | oldToCredits.toInt256(); |
| 724 | _adjustGlobals(creditsChange, fromBalance.toInt256()); |
| 725 | emit YieldUndelegated(_from, to); |
| 726 | } |
| 727 | } |
| 728 |
0.0%
contracts/contracts/utils/Helpers.sol
Lines covered: 0 / 1 (0.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | import { IBasicToken } from "../interfaces/IBasicToken.sol"; |
| 5 | |
| 6 | library Helpers { |
| 7 | /** |
| 8 | * @notice Fetch the `symbol()` from an ERC20 token |
| 9 | * @dev Grabs the `symbol()` from a contract |
| 10 | * @param _token Address of the ERC20 token |
| 11 | * @return string Symbol of the ERC20 token |
| 12 | */ |
| 13 | function getSymbol(address _token) internal view returns (string memory) { |
| 14 | string memory symbol = IBasicToken(_token).symbol(); |
| 15 | return symbol; |
| 16 | } |
| 17 | |
| 18 | /** |
| 19 | * @notice Fetch the `decimals()` from an ERC20 token |
| 20 | * @dev Grabs the `decimals()` from a contract and fails if |
| 21 | * the decimal value does not live within a certain range |
| 22 | * @param _token Address of the ERC20 token |
| 23 | * @return uint256 Decimals of the ERC20 token |
| 24 | */ |
| 25 | function getDecimals(address _token) internal view returns (uint256) { |
| 26 | uint256 decimals = IBasicToken(_token).decimals(); |
| 27 | require( |
| 28 | decimals >= 4 && decimals <= 18, |
| 29 | "Token must have sufficient decimal places" |
| 30 | ); |
| 31 | |
| 32 | return decimals; |
| 33 | } |
| 34 | } |
| 35 |
0.0%
contracts/contracts/utils/Initializable.sol
Lines covered: 0 / 0 (0.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | /** |
| 5 | * @title Base contract any contracts that need to initialize state after deployment. |
| 6 | * @author Origin Protocol Inc |
| 7 | */ |
| 8 | abstract contract Initializable { |
| 9 | /** |
| 10 | * @dev Indicates that the contract has been initialized. |
| 11 | */ |
| 12 | bool private initialized; |
| 13 | |
| 14 | /** |
| 15 | * @dev Indicates that the contract is in the process of being initialized. |
| 16 | */ |
| 17 | bool private initializing; |
| 18 | |
| 19 | /** |
| 20 | * @dev Modifier to protect an initializer function from being invoked twice. |
| 21 | */ |
| 22 | modifier initializer() { |
| 23 | require( |
| 24 | initializing || !initialized, |
| 25 | "Initializable: contract is already initialized" |
| 26 | ); |
| 27 | |
| 28 | bool isTopLevelCall = !initializing; |
| 29 | if (isTopLevelCall) { |
| 30 | initializing = true; |
| 31 | initialized = true; |
| 32 | } |
| 33 | |
| 34 | _; |
| 35 | |
| 36 | if (isTopLevelCall) { |
| 37 | initializing = false; |
| 38 | } |
| 39 | } |
| 40 | |
| 41 | uint256[50] private ______gap; |
| 42 | } |
| 43 |
83.0%
contracts/contracts/utils/StableMath.sol
Lines covered: 5 / 6 (83.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | import { SafeMath } from "@openzeppelin/contracts/utils/math/SafeMath.sol"; |
| 5 | |
| 6 | // Based on StableMath from Stability Labs Pty. Ltd. |
| 7 | // https://github.com/mstable/mStable-contracts/blob/master/contracts/shared/StableMath.sol |
| 8 | |
| 9 | library StableMath { |
| 10 | using SafeMath for uint256; |
| 11 | |
| 12 | /** |
| 13 | * @dev Scaling unit for use in specific calculations, |
| 14 | * where 1 * 10**18, or 1e18 represents a unit '1' |
| 15 | */ |
| 16 | uint256 private constant FULL_SCALE = 1e18; |
| 17 | |
| 18 | /*************************************** |
| 19 | Helpers |
| 20 | ****************************************/ |
| 21 | |
| 22 | /** |
| 23 | * @dev Adjust the scale of an integer |
| 24 | * @param to Decimals to scale to |
| 25 | * @param from Decimals to scale from |
| 26 | */ |
| 27 | function scaleBy( |
| 28 | uint256 x, |
| 29 | uint256 to, |
| 30 | uint256 from |
| 31 | ) internal pure returns (uint256) { |
| 32 | if (to > from) { |
| 33 | x = x.mul(10**(to - from)); |
| 34 | } else if (to < from) { |
| 35 | // slither-disable-next-line divide-before-multiply |
| 36 | x = x.div(10**(from - to)); |
| 37 | } |
| 38 | return x; |
| 39 | } |
| 40 | |
| 41 | /*************************************** |
| 42 | Precise Arithmetic |
| 43 | ****************************************/ |
| 44 | |
| 45 | /** |
| 46 | * @dev Multiplies two precise units, and then truncates by the full scale |
| 47 | * @param x Left hand input to multiplication |
| 48 | * @param y Right hand input to multiplication |
| 49 | * @return Result after multiplying the two inputs and then dividing by the shared |
| 50 | * scale unit |
| 51 | */ |
| 52 | function mulTruncate(uint256 x, uint256 y) internal pure returns (uint256) { |
| 53 | return mulTruncateScale(x, y, FULL_SCALE); |
| 54 | } |
| 55 | |
| 56 | /** |
| 57 | * @dev Multiplies two precise units, and then truncates by the given scale. For example, |
| 58 | * when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18 |
| 59 | * @param x Left hand input to multiplication |
| 60 | * @param y Right hand input to multiplication |
| 61 | * @param scale Scale unit |
| 62 | * @return Result after multiplying the two inputs and then dividing by the shared |
| 63 | * scale unit |
| 64 | */ |
| 65 | function mulTruncateScale( |
| 66 | uint256 x, |
| 67 | uint256 y, |
| 68 | uint256 scale |
| 69 | ) internal pure returns (uint256) { |
| 70 | // e.g. assume scale = fullScale |
| 71 | // z = 10e18 * 9e17 = 9e36 |
| 72 | uint256 z = x.mul(y); |
| 73 | // return 9e36 / 1e18 = 9e18 |
| 74 | return z.div(scale); |
| 75 | } |
| 76 | |
| 77 | /** |
| 78 | * @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result |
| 79 | * @param x Left hand input to multiplication |
| 80 | * @param y Right hand input to multiplication |
| 81 | * @return Result after multiplying the two inputs and then dividing by the shared |
| 82 | * scale unit, rounded up to the closest base unit. |
| 83 | */ |
| 84 | function mulTruncateCeil(uint256 x, uint256 y) |
| 85 | internal |
| 86 | pure |
| 87 | returns (uint256) |
| 88 | { |
| 89 | // e.g. 8e17 * 17268172638 = 138145381104e17 |
| 90 | uint256 scaled = x.mul(y); |
| 91 | // e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17 |
| 92 | uint256 ceil = scaled.add(FULL_SCALE.sub(1)); |
| 93 | // e.g. 13814538111.399...e18 / 1e18 = 13814538111 |
| 94 | return ceil.div(FULL_SCALE); |
| 95 | } |
| 96 | |
| 97 | /** |
| 98 | * @dev Precisely divides two units, by first scaling the left hand operand. Useful |
| 99 | * for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17) |
| 100 | * @param x Left hand input to division |
| 101 | * @param y Right hand input to division |
| 102 | * @return Result after multiplying the left operand by the scale, and |
| 103 | * executing the division on the right hand input. |
| 104 | */ |
| 105 | function divPrecisely(uint256 x, uint256 y) |
| 106 | internal |
| 107 | pure |
| 108 | returns (uint256) |
| 109 | { |
| 110 | // e.g. 8e18 * 1e18 = 8e36 |
| 111 | uint256 z = x.mul(FULL_SCALE); |
| 112 | // e.g. 8e36 / 10e18 = 8e17 |
| 113 | return z.div(y); |
| 114 | } |
| 115 | } |
| 116 |
0.0%
contracts/contracts/vault/VaultStorage.sol
Lines covered: 0 / 0 (0.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | /** |
| 5 | * @title OToken VaultStorage contract |
| 6 | * @notice The VaultStorage contract defines the storage for the Vault contracts |
| 7 | * @author Origin Protocol Inc |
| 8 | */ |
| 9 | |
| 10 | import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; |
| 11 | import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; |
| 12 | import { Address } from "@openzeppelin/contracts/utils/Address.sol"; |
| 13 | |
| 14 | import { IStrategy } from "../interfaces/IStrategy.sol"; |
| 15 | import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; |
| 16 | import { Governable } from "../governance/Governable.sol"; |
| 17 | import { OUSD } from "../token/OUSD.sol"; |
| 18 | import { Initializable } from "../utils/Initializable.sol"; |
| 19 | import "../utils/Helpers.sol"; |
| 20 | |
| 21 | abstract contract VaultStorage is Initializable, Governable { |
| 22 | using SafeERC20 for IERC20; |
| 23 | |
| 24 | event AssetAllocated(address _asset, address _strategy, uint256 _amount); |
| 25 | event StrategyApproved(address _addr); |
| 26 | event StrategyRemoved(address _addr); |
| 27 | event Mint(address _addr, uint256 _value); |
| 28 | event Redeem(address _addr, uint256 _value); |
| 29 | event CapitalPaused(); |
| 30 | event CapitalUnpaused(); |
| 31 | event DefaultStrategyUpdated(address _strategy); |
| 32 | event RebasePaused(); |
| 33 | event RebaseUnpaused(); |
| 34 | event VaultBufferUpdated(uint256 _vaultBuffer); |
| 35 | event AllocateThresholdUpdated(uint256 _threshold); |
| 36 | event StrategistUpdated(address _address); |
| 37 | event MaxSupplyDiffChanged(uint256 maxSupplyDiff); |
| 38 | event YieldDistribution(address _to, uint256 _yield, uint256 _fee); |
| 39 | event TrusteeFeeBpsChanged(uint256 _basis); |
| 40 | event TrusteeAddressChanged(address _address); |
| 41 | event StrategyAddedToMintWhitelist(address indexed strategy); |
| 42 | event StrategyRemovedFromMintWhitelist(address indexed strategy); |
| 43 | event RebasePerSecondMaxChanged(uint256 rebaseRatePerSecond); |
| 44 | event DripDurationChanged(uint256 dripDuration); |
| 45 | event OperatorUpdated(address newOperator); |
| 46 | event WithdrawalRequested( |
| 47 | address indexed _withdrawer, |
| 48 | uint256 indexed _requestId, |
| 49 | uint256 _amount, |
| 50 | uint256 _queued |
| 51 | ); |
| 52 | event WithdrawalClaimed( |
| 53 | address indexed _withdrawer, |
| 54 | uint256 indexed _requestId, |
| 55 | uint256 _amount |
| 56 | ); |
| 57 | event WithdrawalClaimable(uint256 _claimable, uint256 _newClaimable); |
| 58 | event WithdrawalClaimDelayUpdated(uint256 _newDelay); |
| 59 | |
| 60 | // Since we are proxy, all state should be uninitalized. |
| 61 | // Since this storage contract does not have logic directly on it |
| 62 | // we should not be checking for to see if these variables can be constant. |
| 63 | // slither-disable-start uninitialized-state |
| 64 | // slither-disable-start constable-states |
| 65 | |
| 66 | /// @dev mapping of supported vault assets to their configuration |
| 67 | uint256 private _deprecated_assets; |
| 68 | /// @dev list of all assets supported by the vault. |
| 69 | address[] private _deprecated_allAssets; |
| 70 | |
| 71 | // Strategies approved for use by the Vault |
| 72 | struct Strategy { |
| 73 | bool isSupported; |
| 74 | uint256 _deprecated; // Deprecated storage slot |
| 75 | } |
| 76 | /// @dev mapping of strategy contracts to their configuration |
| 77 | mapping(address => Strategy) public strategies; |
| 78 | /// @dev list of all vault strategies |
| 79 | address[] internal allStrategies; |
| 80 | |
| 81 | /// @notice Address of the Oracle price provider contract |
| 82 | address private _deprecated_priceProvider; |
| 83 | /// @notice pause rebasing if true |
| 84 | bool public rebasePaused; |
| 85 | /// @notice pause operations that change the OToken supply. |
| 86 | /// eg mint, redeem, allocate, mint/burn for strategy |
| 87 | bool public capitalPaused; |
| 88 | /// @notice Redemption fee in basis points. eg 50 = 0.5% |
| 89 | uint256 private _deprecated_redeemFeeBps; |
| 90 | /// @notice Percentage of assets to keep in Vault to handle (most) withdrawals. 100% = 1e18. |
| 91 | uint256 public vaultBuffer; |
| 92 | /// @notice OToken mints over this amount automatically allocate funds. 18 decimals. |
| 93 | uint256 public autoAllocateThreshold; |
| 94 | /// @dev Deprecated. Was the auto-rebase trigger threshold for mint/redeem. |
| 95 | /// Storage slot retained for proxy compatibility; no longer read or written. |
| 96 | uint256 internal __deprecatedRebaseThreshold; |
| 97 | |
| 98 | /// @dev Address of the OToken token. eg OUSD or OETH. |
| 99 | OUSD public oToken; |
| 100 | |
| 101 | /// @dev Address of the contract responsible for post rebase syncs with AMMs |
| 102 | address private _deprecated_rebaseHooksAddr = address(0); |
| 103 | |
| 104 | /// @dev Deprecated: Address of Uniswap |
| 105 | address private _deprecated_uniswapAddr = address(0); |
| 106 | |
| 107 | /// @notice Address of the Strategist |
| 108 | address public strategistAddr = address(0); |
| 109 | |
| 110 | /// @notice Mapping of asset address to the Strategy that they should automatically |
| 111 | // be allocated to |
| 112 | uint256 private _deprecated_assetDefaultStrategies; |
| 113 | |
| 114 | /// @notice Max difference between total supply and total value of assets. 18 decimals. |
| 115 | uint256 public maxSupplyDiff; |
| 116 | |
| 117 | /// @notice Trustee contract that can collect a percentage of yield |
| 118 | address public trusteeAddress; |
| 119 | |
| 120 | /// @notice Amount of yield collected in basis points. eg 2000 = 20% |
| 121 | uint256 public trusteeFeeBps; |
| 122 | |
| 123 | /// @dev Deprecated: Tokens that should be swapped for stablecoins |
| 124 | address[] private _deprecated_swapTokens; |
| 125 | |
| 126 | /// @notice Metapool strategy that is allowed to mint/burn OTokens without changing collateral |
| 127 | |
| 128 | address private _deprecated_ousdMetaStrategy; |
| 129 | |
| 130 | /// @notice How much OTokens are currently minted by the strategy |
| 131 | int256 private _deprecated_netOusdMintedForStrategy; |
| 132 | |
| 133 | /// @notice How much net total OTokens are allowed to be minted by all strategies |
| 134 | uint256 private _deprecated_netOusdMintForStrategyThreshold; |
| 135 | |
| 136 | uint256 private _deprecated_swapConfig; |
| 137 | |
| 138 | // List of strategies that can mint oTokens directly |
| 139 | // Used in OETHBaseVaultCore |
| 140 | mapping(address => bool) public isMintWhitelistedStrategy; |
| 141 | |
| 142 | /// @notice Address of the Dripper contract that streams harvested rewards to the Vault |
| 143 | /// @dev The vault is proxied so needs to be set with setDripper against the proxy contract. |
| 144 | address private _deprecated_dripper; |
| 145 | |
| 146 | /// Withdrawal Queue Storage ///// |
| 147 | |
| 148 | struct WithdrawalQueueMetadata { |
| 149 | // cumulative total of all withdrawal requests included the ones that have already been claimed |
| 150 | uint128 queued; |
| 151 | // cumulative total of all the requests that can be claimed including the ones that have already been claimed |
| 152 | uint128 claimable; |
| 153 | // total of all the requests that have been claimed |
| 154 | uint128 claimed; |
| 155 | // index of the next withdrawal request starting at 0 |
| 156 | uint128 nextWithdrawalIndex; |
| 157 | } |
| 158 | |
| 159 | /// @notice Global metadata for the withdrawal queue including: |
| 160 | /// queued - cumulative total of all withdrawal requests included the ones that have already been claimed |
| 161 | /// claimable - cumulative total of all the requests that can be claimed including the ones already claimed |
| 162 | /// claimed - total of all the requests that have been claimed |
| 163 | /// nextWithdrawalIndex - index of the next withdrawal request starting at 0 |
| 164 | WithdrawalQueueMetadata public withdrawalQueueMetadata; |
| 165 | |
| 166 | struct WithdrawalRequest { |
| 167 | address withdrawer; |
| 168 | bool claimed; |
| 169 | uint40 timestamp; // timestamp of the withdrawal request |
| 170 | // Amount of oTokens to redeem. eg OETH |
| 171 | uint128 amount; |
| 172 | // cumulative total of all withdrawal requests including this one. |
| 173 | // this request can be claimed when this queued amount is less than or equal to the queue's claimable amount. |
| 174 | uint128 queued; |
| 175 | } |
| 176 | |
| 177 | /// @notice Mapping of withdrawal request indices to the user withdrawal request data |
| 178 | mapping(uint256 => WithdrawalRequest) public withdrawalRequests; |
| 179 | |
| 180 | /// @notice Sets a minimum delay that is required to elapse between |
| 181 | /// requesting async withdrawals and claiming the request. |
| 182 | /// When set to 0 async withdrawals are disabled. |
| 183 | uint256 public withdrawalClaimDelay; |
| 184 | |
| 185 | /// @notice Time in seconds that the vault last rebased yield. |
| 186 | uint64 public lastRebase; |
| 187 | |
| 188 | /// @notice Automatic rebase yield calculations. In seconds. Set to 0 or 1 to disable. |
| 189 | uint64 public dripDuration; |
| 190 | |
| 191 | /// @notice max rebase percentage per second |
| 192 | /// Can be used to set maximum yield of the protocol, |
| 193 | /// spreading out yield over time |
| 194 | uint64 public rebasePerSecondMax; |
| 195 | |
| 196 | /// @notice target rebase rate limit, based on past rates and funds available. |
| 197 | uint64 public rebasePerSecondTarget; |
| 198 | |
| 199 | uint256 internal constant MAX_REBASE = 0.02 ether; |
| 200 | uint256 internal constant MAX_REBASE_PER_SECOND = |
| 201 | uint256(0.05 ether) / 1 days; |
| 202 | |
| 203 | /// @notice Default strategy for asset |
| 204 | address public defaultStrategy; |
| 205 | |
| 206 | /// @notice Address authorized to call `rebase()` directly. The Governor |
| 207 | /// and Strategist are always allowed in addition to this address. |
| 208 | address public operatorAddr; |
| 209 | |
| 210 | // For future use |
| 211 | uint256[41] private __gap; |
| 212 | |
| 213 | /// @notice Index of WETH asset in allAssets array |
| 214 | /// Legacy OETHVaultCore code, relocated here for vault consistency. |
| 215 | uint256 private _deprecated_wethAssetIndex; |
| 216 | |
| 217 | /// @dev Address of the asset (eg. WETH or USDC) |
| 218 | address public immutable asset; |
| 219 | uint8 internal immutable assetDecimals; |
| 220 | |
| 221 | // slither-disable-end constable-states |
| 222 | // slither-disable-end uninitialized-state |
| 223 | |
| 224 | constructor(address _asset) { |
| 225 | uint8 _decimals = IERC20Metadata(_asset).decimals(); |
| 226 | require(_decimals <= 18, "invalid asset decimals"); |
| 227 | asset = _asset; |
| 228 | assetDecimals = _decimals; |
| 229 | } |
| 230 | |
| 231 | /// @notice Deprecated: use `oToken()` instead. |
| 232 | function oUSD() external view returns (OUSD) { |
| 233 | return oToken; |
| 234 | } |
| 235 | } |
| 236 |
0.0%
lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol
Lines covered: 0 / 0 (0.0%)
| 1 | // SPDX-License-Identifier: MIT |
| 2 | // OpenZeppelin Contracts v4.4.1 (token/ERC20/IERC20.sol) |
| 3 | |
| 4 | pragma solidity ^0.8.0; |
| 5 | |
| 6 | /** |
| 7 | * @dev Interface of the ERC20 standard as defined in the EIP. |
| 8 | */ |
| 9 | interface IERC20 { |
| 10 | /** |
| 11 | * @dev Returns the amount of tokens in existence. |
| 12 | */ |
| 13 | function totalSupply() external view returns (uint256); |
| 14 | |
| 15 | /** |
| 16 | * @dev Returns the amount of tokens owned by `account`. |
| 17 | */ |
| 18 | function balanceOf(address account) external view returns (uint256); |
| 19 | |
| 20 | /** |
| 21 | * @dev Moves `amount` tokens from the caller's account to `recipient`. |
| 22 | * |
| 23 | * Returns a boolean value indicating whether the operation succeeded. |
| 24 | * |
| 25 | * Emits a {Transfer} event. |
| 26 | */ |
| 27 | function transfer(address recipient, uint256 amount) external returns (bool); |
| 28 | |
| 29 | /** |
| 30 | * @dev Returns the remaining number of tokens that `spender` will be |
| 31 | * allowed to spend on behalf of `owner` through {transferFrom}. This is |
| 32 | * zero by default. |
| 33 | * |
| 34 | * This value changes when {approve} or {transferFrom} are called. |
| 35 | */ |
| 36 | function allowance(address owner, address spender) external view returns (uint256); |
| 37 | |
| 38 | /** |
| 39 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. |
| 40 | * |
| 41 | * Returns a boolean value indicating whether the operation succeeded. |
| 42 | * |
| 43 | * IMPORTANT: Beware that changing an allowance with this method brings the risk |
| 44 | * that someone may use both the old and the new allowance by unfortunate |
| 45 | * transaction ordering. One possible solution to mitigate this race |
| 46 | * condition is to first reduce the spender's allowance to 0 and set the |
| 47 | * desired value afterwards: |
| 48 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 |
| 49 | * |
| 50 | * Emits an {Approval} event. |
| 51 | */ |
| 52 | function approve(address spender, uint256 amount) external returns (bool); |
| 53 | |
| 54 | /** |
| 55 | * @dev Moves `amount` tokens from `sender` to `recipient` using the |
| 56 | * allowance mechanism. `amount` is then deducted from the caller's |
| 57 | * allowance. |
| 58 | * |
| 59 | * Returns a boolean value indicating whether the operation succeeded. |
| 60 | * |
| 61 | * Emits a {Transfer} event. |
| 62 | */ |
| 63 | function transferFrom( |
| 64 | address sender, |
| 65 | address recipient, |
| 66 | uint256 amount |
| 67 | ) external returns (bool); |
| 68 | |
| 69 | /** |
| 70 | * @dev Emitted when `value` tokens are moved from one account (`from`) to |
| 71 | * another (`to`). |
| 72 | * |
| 73 | * Note that `value` may be zero. |
| 74 | */ |
| 75 | event Transfer(address indexed from, address indexed to, uint256 value); |
| 76 | |
| 77 | /** |
| 78 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by |
| 79 | * a call to {approve}. `value` is the new allowance. |
| 80 | */ |
| 81 | event Approval(address indexed owner, address indexed spender, uint256 value); |
| 82 | } |
| 83 |
0.0%
lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol
Lines covered: 0 / 0 (0.0%)
| 1 | // SPDX-License-Identifier: MIT |
| 2 | // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) |
| 3 | |
| 4 | pragma solidity ^0.8.0; |
| 5 | |
| 6 | import "../IERC20.sol"; |
| 7 | |
| 8 | /** |
| 9 | * @dev Interface for the optional metadata functions from the ERC20 standard. |
| 10 | * |
| 11 | * _Available since v4.1._ |
| 12 | */ |
| 13 | interface IERC20Metadata is IERC20 { |
| 14 | /** |
| 15 | * @dev Returns the name of the token. |
| 16 | */ |
| 17 | function name() external view returns (string memory); |
| 18 | |
| 19 | /** |
| 20 | * @dev Returns the symbol of the token. |
| 21 | */ |
| 22 | function symbol() external view returns (string memory); |
| 23 | |
| 24 | /** |
| 25 | * @dev Returns the decimals places of the token. |
| 26 | */ |
| 27 | function decimals() external view returns (uint8); |
| 28 | } |
| 29 |
0.0%
lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol
Lines covered: 0 / 1 (0.0%)
| 1 | // SPDX-License-Identifier: MIT |
| 2 | // OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol) |
| 3 | |
| 4 | pragma solidity ^0.8.0; |
| 5 | |
| 6 | import "../IERC20.sol"; |
| 7 | import "../../../utils/Address.sol"; |
| 8 | |
| 9 | /** |
| 10 | * @title SafeERC20 |
| 11 | * @dev Wrappers around ERC20 operations that throw on failure (when the token |
| 12 | * contract returns false). Tokens that return no value (and instead revert or |
| 13 | * throw on failure) are also supported, non-reverting calls are assumed to be |
| 14 | * successful. |
| 15 | * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, |
| 16 | * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. |
| 17 | */ |
| 18 | library SafeERC20 { |
| 19 | using Address for address; |
| 20 | |
| 21 | function safeTransfer( |
| 22 | IERC20 token, |
| 23 | address to, |
| 24 | uint256 value |
| 25 | ) internal { |
| 26 | _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); |
| 27 | } |
| 28 | |
| 29 | function safeTransferFrom( |
| 30 | IERC20 token, |
| 31 | address from, |
| 32 | address to, |
| 33 | uint256 value |
| 34 | ) internal { |
| 35 | _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); |
| 36 | } |
| 37 | |
| 38 | /** |
| 39 | * @dev Deprecated. This function has issues similar to the ones found in |
| 40 | * {IERC20-approve}, and its usage is discouraged. |
| 41 | * |
| 42 | * Whenever possible, use {safeIncreaseAllowance} and |
| 43 | * {safeDecreaseAllowance} instead. |
| 44 | */ |
| 45 | function safeApprove( |
| 46 | IERC20 token, |
| 47 | address spender, |
| 48 | uint256 value |
| 49 | ) internal { |
| 50 | // safeApprove should only be called when setting an initial allowance, |
| 51 | // or when resetting it to zero. To increase and decrease it, use |
| 52 | // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' |
| 53 | require( |
| 54 | (value == 0) || (token.allowance(address(this), spender) == 0), |
| 55 | "SafeERC20: approve from non-zero to non-zero allowance" |
| 56 | ); |
| 57 | _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); |
| 58 | } |
| 59 | |
| 60 | function safeIncreaseAllowance( |
| 61 | IERC20 token, |
| 62 | address spender, |
| 63 | uint256 value |
| 64 | ) internal { |
| 65 | uint256 newAllowance = token.allowance(address(this), spender) + value; |
| 66 | _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); |
| 67 | } |
| 68 | |
| 69 | function safeDecreaseAllowance( |
| 70 | IERC20 token, |
| 71 | address spender, |
| 72 | uint256 value |
| 73 | ) internal { |
| 74 | unchecked { |
| 75 | uint256 oldAllowance = token.allowance(address(this), spender); |
| 76 | require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); |
| 77 | uint256 newAllowance = oldAllowance - value; |
| 78 | _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | /** |
| 83 | * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement |
| 84 | * on the return value: the return value is optional (but if data is returned, it must not be false). |
| 85 | * @param token The token targeted by the call. |
| 86 | * @param data The call data (encoded using abi.encode or one of its variants). |
| 87 | */ |
| 88 | function _callOptionalReturn(IERC20 token, bytes memory data) private { |
| 89 | // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since |
| 90 | // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that |
| 91 | // the target address contains contract code and also asserts for success in the low-level call. |
| 92 | |
| 93 | bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); |
| 94 | if (returndata.length > 0) { |
| 95 | // Return data is optional |
| 96 | require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); |
| 97 | } |
| 98 | } |
| 99 | } |
| 100 |
0.0%
lib/openzeppelin-contracts/contracts/utils/Address.sol
Lines covered: 0 / 1 (0.0%)
| 1 | // SPDX-License-Identifier: MIT |
| 2 | // OpenZeppelin Contracts v4.4.1 (utils/Address.sol) |
| 3 | |
| 4 | pragma solidity ^0.8.0; |
| 5 | |
| 6 | /** |
| 7 | * @dev Collection of functions related to the address type |
| 8 | */ |
| 9 | library Address { |
| 10 | /** |
| 11 | * @dev Returns true if `account` is a contract. |
| 12 | * |
| 13 | * [IMPORTANT] |
| 14 | * ==== |
| 15 | * It is unsafe to assume that an address for which this function returns |
| 16 | * false is an externally-owned account (EOA) and not a contract. |
| 17 | * |
| 18 | * Among others, `isContract` will return false for the following |
| 19 | * types of addresses: |
| 20 | * |
| 21 | * - an externally-owned account |
| 22 | * - a contract in construction |
| 23 | * - an address where a contract will be created |
| 24 | * - an address where a contract lived, but was destroyed |
| 25 | * ==== |
| 26 | */ |
| 27 | function isContract(address account) internal view returns (bool) { |
| 28 | // This method relies on extcodesize, which returns 0 for contracts in |
| 29 | // construction, since the code is only stored at the end of the |
| 30 | // constructor execution. |
| 31 | |
| 32 | uint256 size; |
| 33 | assembly { |
| 34 | size := extcodesize(account) |
| 35 | } |
| 36 | return size > 0; |
| 37 | } |
| 38 | |
| 39 | /** |
| 40 | * @dev Replacement for Solidity's `transfer`: sends `amount` wei to |
| 41 | * `recipient`, forwarding all available gas and reverting on errors. |
| 42 | * |
| 43 | * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost |
| 44 | * of certain opcodes, possibly making contracts go over the 2300 gas limit |
| 45 | * imposed by `transfer`, making them unable to receive funds via |
| 46 | * `transfer`. {sendValue} removes this limitation. |
| 47 | * |
| 48 | * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. |
| 49 | * |
| 50 | * IMPORTANT: because control is transferred to `recipient`, care must be |
| 51 | * taken to not create reentrancy vulnerabilities. Consider using |
| 52 | * {ReentrancyGuard} or the |
| 53 | * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. |
| 54 | */ |
| 55 | function sendValue(address payable recipient, uint256 amount) internal { |
| 56 | require(address(this).balance >= amount, "Address: insufficient balance"); |
| 57 | |
| 58 | (bool success, ) = recipient.call{value: amount}(""); |
| 59 | require(success, "Address: unable to send value, recipient may have reverted"); |
| 60 | } |
| 61 | |
| 62 | /** |
| 63 | * @dev Performs a Solidity function call using a low level `call`. A |
| 64 | * plain `call` is an unsafe replacement for a function call: use this |
| 65 | * function instead. |
| 66 | * |
| 67 | * If `target` reverts with a revert reason, it is bubbled up by this |
| 68 | * function (like regular Solidity function calls). |
| 69 | * |
| 70 | * Returns the raw returned data. To convert to the expected return value, |
| 71 | * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. |
| 72 | * |
| 73 | * Requirements: |
| 74 | * |
| 75 | * - `target` must be a contract. |
| 76 | * - calling `target` with `data` must not revert. |
| 77 | * |
| 78 | * _Available since v3.1._ |
| 79 | */ |
| 80 | function functionCall(address target, bytes memory data) internal returns (bytes memory) { |
| 81 | return functionCall(target, data, "Address: low-level call failed"); |
| 82 | } |
| 83 | |
| 84 | /** |
| 85 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with |
| 86 | * `errorMessage` as a fallback revert reason when `target` reverts. |
| 87 | * |
| 88 | * _Available since v3.1._ |
| 89 | */ |
| 90 | function functionCall( |
| 91 | address target, |
| 92 | bytes memory data, |
| 93 | string memory errorMessage |
| 94 | ) internal returns (bytes memory) { |
| 95 | return functionCallWithValue(target, data, 0, errorMessage); |
| 96 | } |
| 97 | |
| 98 | /** |
| 99 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], |
| 100 | * but also transferring `value` wei to `target`. |
| 101 | * |
| 102 | * Requirements: |
| 103 | * |
| 104 | * - the calling contract must have an ETH balance of at least `value`. |
| 105 | * - the called Solidity function must be `payable`. |
| 106 | * |
| 107 | * _Available since v3.1._ |
| 108 | */ |
| 109 | function functionCallWithValue( |
| 110 | address target, |
| 111 | bytes memory data, |
| 112 | uint256 value |
| 113 | ) internal returns (bytes memory) { |
| 114 | return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); |
| 115 | } |
| 116 | |
| 117 | /** |
| 118 | * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but |
| 119 | * with `errorMessage` as a fallback revert reason when `target` reverts. |
| 120 | * |
| 121 | * _Available since v3.1._ |
| 122 | */ |
| 123 | function functionCallWithValue( |
| 124 | address target, |
| 125 | bytes memory data, |
| 126 | uint256 value, |
| 127 | string memory errorMessage |
| 128 | ) internal returns (bytes memory) { |
| 129 | require(address(this).balance >= value, "Address: insufficient balance for call"); |
| 130 | require(isContract(target), "Address: call to non-contract"); |
| 131 | |
| 132 | (bool success, bytes memory returndata) = target.call{value: value}(data); |
| 133 | return verifyCallResult(success, returndata, errorMessage); |
| 134 | } |
| 135 | |
| 136 | /** |
| 137 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], |
| 138 | * but performing a static call. |
| 139 | * |
| 140 | * _Available since v3.3._ |
| 141 | */ |
| 142 | function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { |
| 143 | return functionStaticCall(target, data, "Address: low-level static call failed"); |
| 144 | } |
| 145 | |
| 146 | /** |
| 147 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], |
| 148 | * but performing a static call. |
| 149 | * |
| 150 | * _Available since v3.3._ |
| 151 | */ |
| 152 | function functionStaticCall( |
| 153 | address target, |
| 154 | bytes memory data, |
| 155 | string memory errorMessage |
| 156 | ) internal view returns (bytes memory) { |
| 157 | require(isContract(target), "Address: static call to non-contract"); |
| 158 | |
| 159 | (bool success, bytes memory returndata) = target.staticcall(data); |
| 160 | return verifyCallResult(success, returndata, errorMessage); |
| 161 | } |
| 162 | |
| 163 | /** |
| 164 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], |
| 165 | * but performing a delegate call. |
| 166 | * |
| 167 | * _Available since v3.4._ |
| 168 | */ |
| 169 | function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { |
| 170 | return functionDelegateCall(target, data, "Address: low-level delegate call failed"); |
| 171 | } |
| 172 | |
| 173 | /** |
| 174 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], |
| 175 | * but performing a delegate call. |
| 176 | * |
| 177 | * _Available since v3.4._ |
| 178 | */ |
| 179 | function functionDelegateCall( |
| 180 | address target, |
| 181 | bytes memory data, |
| 182 | string memory errorMessage |
| 183 | ) internal returns (bytes memory) { |
| 184 | require(isContract(target), "Address: delegate call to non-contract"); |
| 185 | |
| 186 | (bool success, bytes memory returndata) = target.delegatecall(data); |
| 187 | return verifyCallResult(success, returndata, errorMessage); |
| 188 | } |
| 189 | |
| 190 | /** |
| 191 | * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the |
| 192 | * revert reason using the provided one. |
| 193 | * |
| 194 | * _Available since v4.3._ |
| 195 | */ |
| 196 | function verifyCallResult( |
| 197 | bool success, |
| 198 | bytes memory returndata, |
| 199 | string memory errorMessage |
| 200 | ) internal pure returns (bytes memory) { |
| 201 | if (success) { |
| 202 | return returndata; |
| 203 | } else { |
| 204 | // Look for revert reason and bubble it up if present |
| 205 | if (returndata.length > 0) { |
| 206 | // The easiest way to bubble the revert reason is using memory via assembly |
| 207 | |
| 208 | assembly { |
| 209 | let returndata_size := mload(returndata) |
| 210 | revert(add(32, returndata), returndata_size) |
| 211 | } |
| 212 | } else { |
| 213 | revert(errorMessage); |
| 214 | } |
| 215 | } |
| 216 | } |
| 217 | } |
| 218 |
83.0%
lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol
Lines covered: 5 / 6 (83.0%)
| 1 | // SPDX-License-Identifier: MIT |
| 2 | // OpenZeppelin Contracts v4.4.1 (utils/math/SafeCast.sol) |
| 3 | |
| 4 | pragma solidity ^0.8.0; |
| 5 | |
| 6 | /** |
| 7 | * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow |
| 8 | * checks. |
| 9 | * |
| 10 | * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can |
| 11 | * easily result in undesired exploitation or bugs, since developers usually |
| 12 | * assume that overflows raise errors. `SafeCast` restores this intuition by |
| 13 | * reverting the transaction when such an operation overflows. |
| 14 | * |
| 15 | * Using this library instead of the unchecked operations eliminates an entire |
| 16 | * class of bugs, so it's recommended to use it always. |
| 17 | * |
| 18 | * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing |
| 19 | * all math on `uint256` and `int256` and then downcasting. |
| 20 | */ |
| 21 | library SafeCast { |
| 22 | /** |
| 23 | * @dev Returns the downcasted uint224 from uint256, reverting on |
| 24 | * overflow (when the input is greater than largest uint224). |
| 25 | * |
| 26 | * Counterpart to Solidity's `uint224` operator. |
| 27 | * |
| 28 | * Requirements: |
| 29 | * |
| 30 | * - input must fit into 224 bits |
| 31 | */ |
| 32 | function toUint224(uint256 value) internal pure returns (uint224) { |
| 33 | require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); |
| 34 | return uint224(value); |
| 35 | } |
| 36 | |
| 37 | /** |
| 38 | * @dev Returns the downcasted uint128 from uint256, reverting on |
| 39 | * overflow (when the input is greater than largest uint128). |
| 40 | * |
| 41 | * Counterpart to Solidity's `uint128` operator. |
| 42 | * |
| 43 | * Requirements: |
| 44 | * |
| 45 | * - input must fit into 128 bits |
| 46 | */ |
| 47 | function toUint128(uint256 value) internal pure returns (uint128) { |
| 48 | require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); |
| 49 | return uint128(value); |
| 50 | } |
| 51 | |
| 52 | /** |
| 53 | * @dev Returns the downcasted uint96 from uint256, reverting on |
| 54 | * overflow (when the input is greater than largest uint96). |
| 55 | * |
| 56 | * Counterpart to Solidity's `uint96` operator. |
| 57 | * |
| 58 | * Requirements: |
| 59 | * |
| 60 | * - input must fit into 96 bits |
| 61 | */ |
| 62 | function toUint96(uint256 value) internal pure returns (uint96) { |
| 63 | require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); |
| 64 | return uint96(value); |
| 65 | } |
| 66 | |
| 67 | /** |
| 68 | * @dev Returns the downcasted uint64 from uint256, reverting on |
| 69 | * overflow (when the input is greater than largest uint64). |
| 70 | * |
| 71 | * Counterpart to Solidity's `uint64` operator. |
| 72 | * |
| 73 | * Requirements: |
| 74 | * |
| 75 | * - input must fit into 64 bits |
| 76 | */ |
| 77 | function toUint64(uint256 value) internal pure returns (uint64) { |
| 78 | require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); |
| 79 | return uint64(value); |
| 80 | } |
| 81 | |
| 82 | /** |
| 83 | * @dev Returns the downcasted uint32 from uint256, reverting on |
| 84 | * overflow (when the input is greater than largest uint32). |
| 85 | * |
| 86 | * Counterpart to Solidity's `uint32` operator. |
| 87 | * |
| 88 | * Requirements: |
| 89 | * |
| 90 | * - input must fit into 32 bits |
| 91 | */ |
| 92 | function toUint32(uint256 value) internal pure returns (uint32) { |
| 93 | require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); |
| 94 | return uint32(value); |
| 95 | } |
| 96 | |
| 97 | /** |
| 98 | * @dev Returns the downcasted uint16 from uint256, reverting on |
| 99 | * overflow (when the input is greater than largest uint16). |
| 100 | * |
| 101 | * Counterpart to Solidity's `uint16` operator. |
| 102 | * |
| 103 | * Requirements: |
| 104 | * |
| 105 | * - input must fit into 16 bits |
| 106 | */ |
| 107 | function toUint16(uint256 value) internal pure returns (uint16) { |
| 108 | require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); |
| 109 | return uint16(value); |
| 110 | } |
| 111 | |
| 112 | /** |
| 113 | * @dev Returns the downcasted uint8 from uint256, reverting on |
| 114 | * overflow (when the input is greater than largest uint8). |
| 115 | * |
| 116 | * Counterpart to Solidity's `uint8` operator. |
| 117 | * |
| 118 | * Requirements: |
| 119 | * |
| 120 | * - input must fit into 8 bits. |
| 121 | */ |
| 122 | function toUint8(uint256 value) internal pure returns (uint8) { |
| 123 | require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); |
| 124 | return uint8(value); |
| 125 | } |
| 126 | |
| 127 | /** |
| 128 | * @dev Converts a signed int256 into an unsigned uint256. |
| 129 | * |
| 130 | * Requirements: |
| 131 | * |
| 132 | * - input must be greater than or equal to 0. |
| 133 | */ |
| 134 | function toUint256(int256 value) internal pure returns (uint256) { |
| 135 | require(value >= 0, "SafeCast: value must be positive"); |
| 136 | return uint256(value); |
| 137 | } |
| 138 | |
| 139 | /** |
| 140 | * @dev Returns the downcasted int128 from int256, reverting on |
| 141 | * overflow (when the input is less than smallest int128 or |
| 142 | * greater than largest int128). |
| 143 | * |
| 144 | * Counterpart to Solidity's `int128` operator. |
| 145 | * |
| 146 | * Requirements: |
| 147 | * |
| 148 | * - input must fit into 128 bits |
| 149 | * |
| 150 | * _Available since v3.1._ |
| 151 | */ |
| 152 | function toInt128(int256 value) internal pure returns (int128) { |
| 153 | require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits"); |
| 154 | return int128(value); |
| 155 | } |
| 156 | |
| 157 | /** |
| 158 | * @dev Returns the downcasted int64 from int256, reverting on |
| 159 | * overflow (when the input is less than smallest int64 or |
| 160 | * greater than largest int64). |
| 161 | * |
| 162 | * Counterpart to Solidity's `int64` operator. |
| 163 | * |
| 164 | * Requirements: |
| 165 | * |
| 166 | * - input must fit into 64 bits |
| 167 | * |
| 168 | * _Available since v3.1._ |
| 169 | */ |
| 170 | function toInt64(int256 value) internal pure returns (int64) { |
| 171 | require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits"); |
| 172 | return int64(value); |
| 173 | } |
| 174 | |
| 175 | /** |
| 176 | * @dev Returns the downcasted int32 from int256, reverting on |
| 177 | * overflow (when the input is less than smallest int32 or |
| 178 | * greater than largest int32). |
| 179 | * |
| 180 | * Counterpart to Solidity's `int32` operator. |
| 181 | * |
| 182 | * Requirements: |
| 183 | * |
| 184 | * - input must fit into 32 bits |
| 185 | * |
| 186 | * _Available since v3.1._ |
| 187 | */ |
| 188 | function toInt32(int256 value) internal pure returns (int32) { |
| 189 | require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits"); |
| 190 | return int32(value); |
| 191 | } |
| 192 | |
| 193 | /** |
| 194 | * @dev Returns the downcasted int16 from int256, reverting on |
| 195 | * overflow (when the input is less than smallest int16 or |
| 196 | * greater than largest int16). |
| 197 | * |
| 198 | * Counterpart to Solidity's `int16` operator. |
| 199 | * |
| 200 | * Requirements: |
| 201 | * |
| 202 | * - input must fit into 16 bits |
| 203 | * |
| 204 | * _Available since v3.1._ |
| 205 | */ |
| 206 | function toInt16(int256 value) internal pure returns (int16) { |
| 207 | require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits"); |
| 208 | return int16(value); |
| 209 | } |
| 210 | |
| 211 | /** |
| 212 | * @dev Returns the downcasted int8 from int256, reverting on |
| 213 | * overflow (when the input is less than smallest int8 or |
| 214 | * greater than largest int8). |
| 215 | * |
| 216 | * Counterpart to Solidity's `int8` operator. |
| 217 | * |
| 218 | * Requirements: |
| 219 | * |
| 220 | * - input must fit into 8 bits. |
| 221 | * |
| 222 | * _Available since v3.1._ |
| 223 | */ |
| 224 | function toInt8(int256 value) internal pure returns (int8) { |
| 225 | require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits"); |
| 226 | return int8(value); |
| 227 | } |
| 228 | |
| 229 | /** |
| 230 | * @dev Converts an unsigned uint256 into a signed int256. |
| 231 | * |
| 232 | * Requirements: |
| 233 | * |
| 234 | * - input must be less than or equal to maxInt256. |
| 235 | */ |
| 236 | function toInt256(uint256 value) internal pure returns (int256) { |
| 237 | // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive |
| 238 | require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); |
| 239 | return int256(value); |
| 240 | } |
| 241 | } |
| 242 |
80.0%
lib/openzeppelin-contracts/contracts/utils/math/SafeMath.sol
Lines covered: 4 / 5 (80.0%)
| 1 | // SPDX-License-Identifier: MIT |
| 2 | // OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol) |
| 3 | |
| 4 | pragma solidity ^0.8.0; |
| 5 | |
| 6 | // CAUTION |
| 7 | // This version of SafeMath should only be used with Solidity 0.8 or later, |
| 8 | // because it relies on the compiler's built in overflow checks. |
| 9 | |
| 10 | /** |
| 11 | * @dev Wrappers over Solidity's arithmetic operations. |
| 12 | * |
| 13 | * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler |
| 14 | * now has built in overflow checking. |
| 15 | */ |
| 16 | library SafeMath { |
| 17 | /** |
| 18 | * @dev Returns the addition of two unsigned integers, with an overflow flag. |
| 19 | * |
| 20 | * _Available since v3.4._ |
| 21 | */ |
| 22 | function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { |
| 23 | unchecked { |
| 24 | uint256 c = a + b; |
| 25 | if (c < a) return (false, 0); |
| 26 | return (true, c); |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | /** |
| 31 | * @dev Returns the substraction of two unsigned integers, with an overflow flag. |
| 32 | * |
| 33 | * _Available since v3.4._ |
| 34 | */ |
| 35 | function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { |
| 36 | unchecked { |
| 37 | if (b > a) return (false, 0); |
| 38 | return (true, a - b); |
| 39 | } |
| 40 | } |
| 41 | |
| 42 | /** |
| 43 | * @dev Returns the multiplication of two unsigned integers, with an overflow flag. |
| 44 | * |
| 45 | * _Available since v3.4._ |
| 46 | */ |
| 47 | function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { |
| 48 | unchecked { |
| 49 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the |
| 50 | // benefit is lost if 'b' is also tested. |
| 51 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 |
| 52 | if (a == 0) return (true, 0); |
| 53 | uint256 c = a * b; |
| 54 | if (c / a != b) return (false, 0); |
| 55 | return (true, c); |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | /** |
| 60 | * @dev Returns the division of two unsigned integers, with a division by zero flag. |
| 61 | * |
| 62 | * _Available since v3.4._ |
| 63 | */ |
| 64 | function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { |
| 65 | unchecked { |
| 66 | if (b == 0) return (false, 0); |
| 67 | return (true, a / b); |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | /** |
| 72 | * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. |
| 73 | * |
| 74 | * _Available since v3.4._ |
| 75 | */ |
| 76 | function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { |
| 77 | unchecked { |
| 78 | if (b == 0) return (false, 0); |
| 79 | return (true, a % b); |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | /** |
| 84 | * @dev Returns the addition of two unsigned integers, reverting on |
| 85 | * overflow. |
| 86 | * |
| 87 | * Counterpart to Solidity's `+` operator. |
| 88 | * |
| 89 | * Requirements: |
| 90 | * |
| 91 | * - Addition cannot overflow. |
| 92 | */ |
| 93 | function add(uint256 a, uint256 b) internal pure returns (uint256) { |
| 94 | return a + b; |
| 95 | } |
| 96 | |
| 97 | /** |
| 98 | * @dev Returns the subtraction of two unsigned integers, reverting on |
| 99 | * overflow (when the result is negative). |
| 100 | * |
| 101 | * Counterpart to Solidity's `-` operator. |
| 102 | * |
| 103 | * Requirements: |
| 104 | * |
| 105 | * - Subtraction cannot overflow. |
| 106 | */ |
| 107 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { |
| 108 | return a - b; |
| 109 | } |
| 110 | |
| 111 | /** |
| 112 | * @dev Returns the multiplication of two unsigned integers, reverting on |
| 113 | * overflow. |
| 114 | * |
| 115 | * Counterpart to Solidity's `*` operator. |
| 116 | * |
| 117 | * Requirements: |
| 118 | * |
| 119 | * - Multiplication cannot overflow. |
| 120 | */ |
| 121 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { |
| 122 | return a * b; |
| 123 | } |
| 124 | |
| 125 | /** |
| 126 | * @dev Returns the integer division of two unsigned integers, reverting on |
| 127 | * division by zero. The result is rounded towards zero. |
| 128 | * |
| 129 | * Counterpart to Solidity's `/` operator. |
| 130 | * |
| 131 | * Requirements: |
| 132 | * |
| 133 | * - The divisor cannot be zero. |
| 134 | */ |
| 135 | function div(uint256 a, uint256 b) internal pure returns (uint256) { |
| 136 | return a / b; |
| 137 | } |
| 138 | |
| 139 | /** |
| 140 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), |
| 141 | * reverting when dividing by zero. |
| 142 | * |
| 143 | * Counterpart to Solidity's `%` operator. This function uses a `revert` |
| 144 | * opcode (which leaves remaining gas untouched) while Solidity uses an |
| 145 | * invalid opcode to revert (consuming all remaining gas). |
| 146 | * |
| 147 | * Requirements: |
| 148 | * |
| 149 | * - The divisor cannot be zero. |
| 150 | */ |
| 151 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { |
| 152 | return a % b; |
| 153 | } |
| 154 | |
| 155 | /** |
| 156 | * @dev Returns the subtraction of two unsigned integers, reverting with custom message on |
| 157 | * overflow (when the result is negative). |
| 158 | * |
| 159 | * CAUTION: This function is deprecated because it requires allocating memory for the error |
| 160 | * message unnecessarily. For custom revert reasons use {trySub}. |
| 161 | * |
| 162 | * Counterpart to Solidity's `-` operator. |
| 163 | * |
| 164 | * Requirements: |
| 165 | * |
| 166 | * - Subtraction cannot overflow. |
| 167 | */ |
| 168 | function sub( |
| 169 | uint256 a, |
| 170 | uint256 b, |
| 171 | string memory errorMessage |
| 172 | ) internal pure returns (uint256) { |
| 173 | unchecked { |
| 174 | require(b <= a, errorMessage); |
| 175 | return a - b; |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | /** |
| 180 | * @dev Returns the integer division of two unsigned integers, reverting with custom message on |
| 181 | * division by zero. The result is rounded towards zero. |
| 182 | * |
| 183 | * Counterpart to Solidity's `/` operator. Note: this function uses a |
| 184 | * `revert` opcode (which leaves remaining gas untouched) while Solidity |
| 185 | * uses an invalid opcode to revert (consuming all remaining gas). |
| 186 | * |
| 187 | * Requirements: |
| 188 | * |
| 189 | * - The divisor cannot be zero. |
| 190 | */ |
| 191 | function div( |
| 192 | uint256 a, |
| 193 | uint256 b, |
| 194 | string memory errorMessage |
| 195 | ) internal pure returns (uint256) { |
| 196 | unchecked { |
| 197 | require(b > 0, errorMessage); |
| 198 | return a / b; |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | /** |
| 203 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), |
| 204 | * reverting with custom message when dividing by zero. |
| 205 | * |
| 206 | * CAUTION: This function is deprecated because it requires allocating memory for the error |
| 207 | * message unnecessarily. For custom revert reasons use {tryMod}. |
| 208 | * |
| 209 | * Counterpart to Solidity's `%` operator. This function uses a `revert` |
| 210 | * opcode (which leaves remaining gas untouched) while Solidity uses an |
| 211 | * invalid opcode to revert (consuming all remaining gas). |
| 212 | * |
| 213 | * Requirements: |
| 214 | * |
| 215 | * - The divisor cannot be zero. |
| 216 | */ |
| 217 | function mod( |
| 218 | uint256 a, |
| 219 | uint256 b, |
| 220 | string memory errorMessage |
| 221 | ) internal pure returns (uint256) { |
| 222 | unchecked { |
| 223 | require(b > 0, errorMessage); |
| 224 | return a % b; |
| 225 | } |
| 226 | } |
| 227 | } |
| 228 |
100.0%
test/recon/CryticTester.sol
Lines covered: 1 / 1 (100.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | import {Properties} from "./Properties.sol"; |
| 5 | |
| 6 | // echidna test/recon/CryticTester.sol --contract CryticTester --config echidna.yaml |
| 7 | contract CryticTester is Properties {} |
| 8 |
88.0%
test/recon/Properties.sol
Lines covered: 8 / 9 (88.0%)
| 1 | // SPDX-License-Identifier: BUSL-1.1 |
| 2 | pragma solidity ^0.8.0; |
| 3 | |
| 4 | import {Echidna} from "../../contracts/contracts/echidna/Echidna.sol"; |
| 5 | |
| 6 | /// @notice scfuzzbench adaptation layer: canary checks on top of the |
| 7 | /// upstream OUSD Echidna suite. Assertion failures are surfaced the same |
| 8 | /// way for Echidna, Medusa, Foundry, and Recon (AssertionFailed event + |
| 9 | /// assert), and dedup to the emitting function name across fuzzers. |
| 10 | abstract contract Properties is Echidna { |
| 11 | string internal constant ASSERTION_CANARY = "!!! canary assertion"; |
| 12 | string internal constant INVARIANT_CANARY_GLOBAL_INVARIANT_FAILURE = "Canary invariant"; |
| 13 | |
| 14 | event AssertionFailed(string reason); |
| 15 | |
| 16 | function t(bool b, string memory reason) internal { |
| 17 | if (!b) { |
| 18 | emit AssertionFailed(reason); |
| 19 | assert(false); |
| 20 | } |
| 21 | } |
| 22 | |
| 23 | function invariant_canary() public returns (bool) { |
| 24 | t(false, INVARIANT_CANARY_GLOBAL_INVARIANT_FAILURE); |
| 25 | return true; |
| 26 | } |
| 27 | |
| 28 | function assert_canary_ASSERTION_CANARY(uint256 entropy) public { |
| 29 | t(entropy > 0, ASSERTION_CANARY); |
| 30 | } |
| 31 | } |
| 32 |