Files
30
Total Lines
3914
Coverage
80.0%
483 / 604 lines
Actions
100.0%
contracts/contracts/echidna/Debugger.sol
Lines covered: 3 / 3 (100.0%)
| 1 |
// SPDX-License-Identifier: BUSL-1.1 |
||
| 2 |
pragma solidity ^0.8.0; |
||
| 3 | |||
| 4 |
✓ 7
|
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 |
✓ 259.1M
|
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 |
✓ 12.9M
|
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: 19 / 19 (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 |
✓ 3
|
contract EchidnaConfig {
|
|
| 9 |
✓ 64.8M
|
address internal constant ADDRESS_VAULT = address(0x10000); |
|
| 10 |
✓ 31.7M
|
address internal constant ADDRESS_OUTSIDER_USER = address(0x20000); |
|
| 11 | |||
| 12 |
✓ 511.9M
|
address internal constant ADDRESS_USER0 = address(0x30000); |
|
| 13 |
✓ 156.9M
|
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 |
✓ 1
|
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 |
✓ 16.0M
|
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 |
✓ 64.8M
|
uint256 internal constant MINT_MODULO = 1_000_000_000_000e18; |
|
| 70 | |||
| 71 |
// Known rounding errors |
||
| 72 |
✓ 4.0M
|
uint256 internal constant TRANSFER_ROUNDING_ERROR = 1e18 - 1; |
|
| 73 |
✓ 4.2M
|
uint256 internal constant OPT_IN_ROUNDING_ERROR = 1e18 - 1; |
|
| 74 |
✓ 10.3M
|
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 |
✓ 74.9M
|
_; |
|
| 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 |
✓ 1.2B
|
returns (address account) |
|
| 103 |
{
|
||
| 104 |
✓ 1.2B
|
accountId = accountId / 64; |
|
| 105 |
✓ 1.2B
|
if (accountId == 0) return account = ADDRESS_USER0; |
|
| 106 |
✓ 641.8M
|
if (accountId == 1) return account = ADDRESS_USER1; |
|
| 107 |
✓ 484.8M
|
if (accountId == 2) return account = ADDRESS_CONTRACT0; |
|
| 108 |
✓ 321.5M
|
if (accountId == 3) return account = ADDRESS_CONTRACT1; |
|
| 109 |
✓ 1.1B
|
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 |
93.7%
contracts/contracts/echidna/EchidnaHelper.sol
Lines covered: 59 / 63 (93.7%)
| 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 |
✓ 64.8M
|
function mint(uint8 toAcc, uint256 amount) public returns (uint256) {
|
|
| 19 |
✓ 64.8M
|
address to = getAccount(toAcc); |
|
| 20 | |||
| 21 |
if (TOGGLE_MINT_LIMIT) {
|
||
| 22 |
✓ 64.8M
|
amount = amount % MINT_MODULO; |
|
| 23 |
} |
||
| 24 | |||
| 25 |
✓ 64.8M
|
hevm.prank(ADDRESS_VAULT); |
|
| 26 |
✓ 64.8M
|
ousd.mint(to, amount); |
|
| 27 | |||
| 28 |
✓ 64.2M
|
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 |
✓ 59.6M
|
address from = getAccount(fromAcc); |
|
| 38 |
✓ 59.6M
|
hevm.prank(ADDRESS_VAULT); |
|
| 39 |
✓ 59.6M
|
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 |
✓ 16.0M
|
ousd.totalSupply() + |
|
| 50 |
✓ 16.0M
|
(amount % (ousd.totalSupply() / CHANGESUPPLY_DIVISOR)); |
|
| 51 |
} |
||
| 52 | |||
| 53 |
✓ 16.0M
|
hevm.prank(ADDRESS_VAULT); |
|
| 54 |
✓ 16.0M
|
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 |
✓ 110.4M
|
address from = getAccount(fromAcc); |
|
| 69 |
✓ 110.4M
|
address to = getAccount(toAcc); |
|
| 70 |
✓ 110.4M
|
hevm.prank(from); |
|
| 71 |
// slither-disable-next-line unchecked-transfer |
||
| 72 |
✓ 110.4M
|
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 |
✓ 17.5M
|
address authorized = getAccount(authorizedAcc); |
|
| 89 |
✓ 17.5M
|
address from = getAccount(fromAcc); |
|
| 90 |
✓ 17.5M
|
address to = getAccount(toAcc); |
|
| 91 |
✓ 17.5M
|
hevm.prank(authorized); |
|
| 92 |
// slither-disable-next-line unchecked-transfer |
||
| 93 |
✓ 17.5M
|
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 |
✓ 61.0M
|
address target = getAccount(targetAcc); |
|
| 102 |
✓ 61.0M
|
hevm.prank(target); |
|
| 103 |
✓ 61.0M
|
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 |
✓ 43.5M
|
address target = getAccount(targetAcc); |
|
| 112 |
✓ 43.5M
|
hevm.prank(target); |
|
| 113 |
✓ 43.5M
|
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 |
✓ 31.8M
|
address owner = getAccount(ownerAcc); |
|
| 128 |
✓ 31.8M
|
address spender = getAccount(spenderAcc); |
|
| 129 |
✓ 31.8M
|
hevm.prank(owner); |
|
| 130 |
// slither-disable-next-line unused-return |
||
| 131 |
✓ 31.8M
|
ousd.approve(spender, amount); |
|
| 132 |
} |
||
| 133 | |||
| 134 |
/** |
||
| 135 |
* @notice Get the sum of all OUSD balances |
||
| 136 |
* @return total Total balance |
||
| 137 |
*/ |
||
| 138 |
✓ 19.2M
|
function getTotalBalance() public view returns (uint256 total) {
|
|
| 139 |
✓ 19.2M
|
total += ousd.balanceOf(ADDRESS_VAULT); |
|
| 140 |
✓ 19.2M
|
total += ousd.balanceOf(ADDRESS_OUTSIDER_USER); |
|
| 141 |
✓ 19.2M
|
total += ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT); |
|
| 142 |
✓ 19.2M
|
total += ousd.balanceOf(ADDRESS_USER0); |
|
| 143 |
✓ 19.2M
|
total += ousd.balanceOf(ADDRESS_USER1); |
|
| 144 |
✓ 19.2M
|
total += ousd.balanceOf(ADDRESS_CONTRACT0); |
|
| 145 |
✓ 19.2M
|
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 |
✓ 31.7M
|
function getTotalNonRebasingBalance() public returns (uint256 total) {
|
|
| 153 |
✓ 31.7M
|
total += ousd._isNonRebasingAccountEchidna(ADDRESS_VAULT) |
|
| 154 |
? ousd.balanceOf(ADDRESS_VAULT) |
||
| 155 |
✓ 31.7M
|
: 0; |
|
| 156 |
✓ 31.7M
|
total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_USER) |
|
| 157 |
? ousd.balanceOf(ADDRESS_OUTSIDER_USER) |
||
| 158 |
✓ 31.7M
|
: 0; |
|
| 159 |
✓ 31.7M
|
total += ousd._isNonRebasingAccountEchidna(ADDRESS_OUTSIDER_CONTRACT) |
|
| 160 |
✓ 31.7M
|
? ousd.balanceOf(ADDRESS_OUTSIDER_CONTRACT) |
|
| 161 |
: 0; |
||
| 162 |
✓ 31.7M
|
total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER0) |
|
| 163 |
✓ 17.6M
|
? ousd.balanceOf(ADDRESS_USER0) |
|
| 164 |
✓ 14.0M
|
: 0; |
|
| 165 |
✓ 31.7M
|
total += ousd._isNonRebasingAccountEchidna(ADDRESS_USER1) |
|
| 166 |
✓ 4.5M
|
? ousd.balanceOf(ADDRESS_USER1) |
|
| 167 |
✓ 27.1M
|
: 0; |
|
| 168 |
✓ 31.7M
|
total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT0) |
|
| 169 |
✓ 26.2M
|
? ousd.balanceOf(ADDRESS_CONTRACT0) |
|
| 170 |
✓ 5.5M
|
: 0; |
|
| 171 |
✓ 31.7M
|
total += ousd._isNonRebasingAccountEchidna(ADDRESS_CONTRACT1) |
|
| 172 |
✓ 10.7M
|
? ousd.balanceOf(ADDRESS_CONTRACT1) |
|
| 173 |
✓ 21.0M
|
: 0; |
|
| 174 |
} |
||
| 175 |
} |
||
| 176 |
93.3%
contracts/contracts/echidna/EchidnaSetup.sol
Lines covered: 14 / 15 (93.3%)
| 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 |
✓ 3
|
contract Dummy {}
|
|
| 9 | |||
| 10 |
/** |
||
| 11 |
* @title Mixin for setup and deployment |
||
| 12 |
* @author Rappie |
||
| 13 |
*/ |
||
| 14 |
contract EchidnaSetup is EchidnaConfig {
|
||
| 15 |
✓ 1
|
IHevm hevm = IHevm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); |
|
| 16 |
✓ 1
|
OUSDEchidna ousd = new OUSDEchidna(); |
|
| 17 | |||
| 18 |
/** |
||
| 19 |
* @notice Deploy the OUSD contract and set up initial state |
||
| 20 |
*/ |
||
| 21 |
constructor() {
|
||
| 22 |
✓ 1
|
ousd.initialize(ADDRESS_VAULT, 1e18); |
|
| 23 | |||
| 24 |
// Deploy dummny contracts as users |
||
| 25 |
✓ 1
|
Dummy outsider = new Dummy(); |
|
| 26 |
✓ 1
|
ADDRESS_OUTSIDER_CONTRACT = address(outsider); |
|
| 27 |
✓ 1
|
Dummy dummy0 = new Dummy(); |
|
| 28 |
✓ 1
|
ADDRESS_CONTRACT0 = address(dummy0); |
|
| 29 |
✓ 1
|
Dummy dummy1 = new Dummy(); |
|
| 30 |
✓ 1
|
ADDRESS_CONTRACT1 = address(dummy1); |
|
| 31 | |||
| 32 |
// Start out with a reasonable amount of OUSD |
||
| 33 |
if (TOGGLE_STARTING_BALANCE) {
|
||
| 34 |
// Rebasing tokens |
||
| 35 |
✓ 1
|
hevm.prank(ADDRESS_VAULT); |
|
| 36 |
✓ 1
|
ousd.mint(ADDRESS_OUTSIDER_USER, STARTING_BALANCE / 2); |
|
| 37 | |||
| 38 |
// Non-rebasing tokens |
||
| 39 |
✓ 1
|
hevm.prank(ADDRESS_VAULT); |
|
| 40 |
✓ 1
|
ousd.mint(ADDRESS_OUTSIDER_CONTRACT, STARTING_BALANCE / 2); |
|
| 41 |
} |
||
| 42 |
} |
||
| 43 |
} |
||
| 44 |
90.9%
contracts/contracts/echidna/EchidnaTestAccounting.sol
Lines covered: 30 / 33 (90.9%)
| 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 |
✓ 19.3M
|
address target = getAccount(targetAcc); |
|
| 18 | |||
| 19 |
✓ 19.3M
|
uint256 balanceBefore = ousd.balanceOf(target); |
|
| 20 |
✓ 19.3M
|
optIn(targetAcc); |
|
| 21 |
✓ 12.1M
|
uint256 balanceAfter = ousd.balanceOf(target); |
|
| 22 | |||
| 23 |
✓ 12.1M
|
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 |
✓ 14.5M
|
address target = getAccount(targetAcc); |
|
| 32 | |||
| 33 |
✓ 14.5M
|
uint256 balanceBefore = ousd.balanceOf(target); |
|
| 34 |
✓ 14.5M
|
optOut(targetAcc); |
|
| 35 |
✓ 22.3M
|
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 |
✓ 14.1M
|
address target = getAccount(targetAcc); |
|
| 46 | |||
| 47 |
✓ 14.1M
|
uint256 balanceBefore = ousd.balanceOf(target); |
|
| 48 |
✓ 14.1M
|
optIn(targetAcc); |
|
| 49 |
✓ 4.8M
|
uint256 balanceAfter = ousd.balanceOf(target); |
|
| 50 | |||
| 51 |
✓ 4.8M
|
int256 delta = int256(balanceAfter) - int256(balanceBefore); |
|
| 52 |
✓ 4.8M
|
Debugger.log("delta", delta);
|
|
| 53 | |||
| 54 |
// slither-disable-next-line tautology |
||
| 55 |
✓ 4.8M
|
assert(-1 * delta >= 0); |
|
| 56 |
✓ 4.2M
|
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 |
✓ 18.1M
|
uint256 totalSupplyBefore = ousd.totalSupply(); |
|
| 65 |
✓ 18.1M
|
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 |
✓ 15.1M
|
uint256 totalSupplyBefore = ousd.totalSupply(); |
|
| 77 |
✓ 15.1M
|
optOut(targetAcc); |
|
| 78 |
✓ 21.6M
|
uint256 totalSupplyAfter = ousd.totalSupply(); |
|
| 79 | |||
| 80 |
✓ 21.6M
|
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 |
✓ 15.0M
|
address target = getAccount(targetAcc); |
|
| 89 | |||
| 90 |
✓ 15.0M
|
uint256 balanceBefore = ousd.balanceOf(target); |
|
| 91 |
// slither-disable-next-line unused-return |
||
| 92 |
✓ 15.0M
|
ousd._isNonRebasingAccountEchidna(target); |
|
| 93 |
✓ 30.0M
|
uint256 balanceAfter = ousd.balanceOf(target); |
|
| 94 | |||
| 95 |
✓ 52.3M
|
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 |
✓ 15.1M
|
address target = getAccount(targetAcc); |
|
| 104 | |||
| 105 |
// slither-disable-next-line unused-return |
||
| 106 |
✓ 15.1M
|
try ousd.balanceOf(target) {
|
|
| 107 |
assert(true); |
||
| 108 |
} catch {
|
||
| 109 |
assert(false); |
||
| 110 |
} |
||
| 111 |
} |
||
| 112 |
} |
||
| 113 |
96.2%
contracts/contracts/echidna/EchidnaTestApproval.sol
Lines covered: 25 / 26 (96.2%)
| 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 |
✓ 11.9M
|
address authorized = getAccount(authorizedAcc); |
|
| 26 |
✓ 11.9M
|
address from = getAccount(fromAcc); |
|
| 27 |
✓ 11.9M
|
address to = getAccount(toAcc); |
|
| 28 | |||
| 29 |
✓ 11.9M
|
require(amount <= ousd.balanceOf(from)); |
|
| 30 |
✓ 935.2K
|
require(amount <= ousd.allowance(from, authorized)); |
|
| 31 | |||
| 32 |
✓ 408.7K
|
hevm.prank(authorized); |
|
| 33 |
// slither-disable-next-line unchecked-transfer |
||
| 34 |
✓ 408.7K
|
try ousd.transferFrom(from, to, amount) {
|
|
| 35 |
// pass |
||
| 36 |
} catch {
|
||
| 37 |
✓ 1.5K
|
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 |
✓ 12.0M
|
address authorized = getAccount(authorizedAcc); |
|
| 55 |
✓ 12.0M
|
address from = getAccount(fromAcc); |
|
| 56 |
✓ 12.0M
|
address to = getAccount(toAcc); |
|
| 57 | |||
| 58 |
✓ 12.0M
|
require(amount > 0); |
|
| 59 |
require( |
||
| 60 |
✓ 11.6M
|
!(amount <= ousd.balanceOf(from) && |
|
| 61 |
✓ 674.2K
|
amount <= ousd.allowance(from, authorized)) |
|
| 62 |
); |
||
| 63 | |||
| 64 |
✓ 11.5M
|
hevm.prank(authorized); |
|
| 65 |
// slither-disable-next-line unchecked-transfer |
||
| 66 |
✓ 11.5M
|
try ousd.transferFrom(from, to, amount) {
|
|
| 67 |
✓ 55.9M
|
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 |
✓ 11.3M
|
address owner = getAccount(ownerAcc); |
|
| 85 |
✓ 11.3M
|
address spender = getAccount(spenderAcc); |
|
| 86 | |||
| 87 |
✓ 11.3M
|
approve(ownerAcc, spenderAcc, amount); |
|
| 88 |
✓ 11.3M
|
uint256 allowanceAfter1 = ousd.allowance(owner, spender); |
|
| 89 | |||
| 90 |
✓ 11.3M
|
assert(allowanceAfter1 == amount); |
|
| 91 | |||
| 92 |
✓ 11.3M
|
approve(ownerAcc, spenderAcc, amount / 2); |
|
| 93 |
✓ 11.3M
|
uint256 allowanceAfter2 = ousd.allowance(owner, spender); |
|
| 94 | |||
| 95 |
✓ 11.3M
|
assert(allowanceAfter2 == amount / 2); |
|
| 96 |
} |
||
| 97 |
} |
||
| 98 |
94.7%
contracts/contracts/echidna/EchidnaTestMintBurn.sol
Lines covered: 36 / 38 (94.7%)
| 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 |
✓ 15.0M
|
address target = getAccount(targetAcc); |
|
| 18 | |||
| 19 |
✓ 15.0M
|
uint256 balanceBefore = ousd.balanceOf(target); |
|
| 20 |
✓ 15.0M
|
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 |
✓ 14.3M
|
address target = getAccount(targetAcc); |
|
| 32 | |||
| 33 |
✓ 14.3M
|
uint256 balanceBefore = ousd.balanceOf(target); |
|
| 34 |
✓ 14.3M
|
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 |
✓ 17.5M
|
address target = getAccount(targetAcc); |
|
| 58 | |||
| 59 |
✓ 17.5M
|
uint256 balanceBefore = ousd.balanceOf(target); |
|
| 60 |
✓ 17.5M
|
uint256 amountMinted = mint(targetAcc, amount); |
|
| 61 |
✓ 17.3M
|
uint256 balanceAfter = ousd.balanceOf(target); |
|
| 62 | |||
| 63 |
✓ 17.3M
|
Debugger.log("amountMinted", amountMinted);
|
|
| 64 |
✓ 17.3M
|
Debugger.log("balanceBefore", balanceBefore);
|
|
| 65 |
✓ 17.3M
|
Debugger.log("balanceAfter", balanceAfter);
|
|
| 66 | |||
| 67 |
✓ 17.3M
|
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 |
✓ 15.7M
|
address target = getAccount(targetAcc); |
|
| 89 | |||
| 90 |
✓ 15.7M
|
uint256 balanceBefore = ousd.balanceOf(target); |
|
| 91 |
✓ 15.7M
|
burn(targetAcc, amount); |
|
| 92 |
✓ 1.0M
|
uint256 balanceAfter = ousd.balanceOf(target); |
|
| 93 | |||
| 94 |
✓ 1.0M
|
Debugger.log("balanceBefore", balanceBefore);
|
|
| 95 |
✓ 1.0M
|
Debugger.log("balanceAfter", balanceAfter);
|
|
| 96 | |||
| 97 |
✓ 1.0M
|
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 |
✓ 13.1M
|
address target = getAccount(targetAcc); |
|
| 107 | |||
| 108 |
✓ 13.1M
|
uint256 balanceBefore = ousd.balanceOf(target); |
|
| 109 |
✓ 13.1M
|
uint256 amountMinted = mint(targetAcc, amount); |
|
| 110 |
✓ 13.0M
|
uint256 balanceAfter = ousd.balanceOf(target); |
|
| 111 | |||
| 112 |
✓ 13.0M
|
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 |
✓ 13.0M
|
int256 error = int256(amountMinted) - delta; |
|
| 118 | |||
| 119 |
✓ 13.0M
|
assert(error >= 0); |
|
| 120 |
✓ 10.3M
|
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 |
✓ 14.4M
|
address target = getAccount(targetAcc); |
|
| 129 | |||
| 130 |
✓ 14.4M
|
burn(targetAcc, ousd.balanceOf(target)); |
|
| 131 |
✓ 14.4M
|
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 |
✓ 20.3M
|
address target = getAccount(targetAcc); |
|
| 143 |
✓ 20.3M
|
uint256 balance = ousd.balanceOf(target); |
|
| 144 | |||
| 145 |
✓ 20.3M
|
hevm.prank(ADDRESS_VAULT); |
|
| 146 |
✓ 20.3M
|
try ousd.burn(target, balance) {
|
|
| 147 |
assert(true); |
||
| 148 |
} catch {
|
||
| 149 |
assert(false); |
||
| 150 |
} |
||
| 151 |
} |
||
| 152 |
} |
||
| 153 |
100.0%
contracts/contracts/echidna/EchidnaTestSupply.sol
Lines covered: 32 / 32 (100.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 |
✓ 1
|
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 |
✓ 28.8M
|
hevm.prank(ADDRESS_VAULT); |
|
| 35 |
✓ 28.8M
|
ousd.changeSupply(supply); |
|
| 36 | |||
| 37 |
✓ 23.3M
|
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 |
✓ 19.0M
|
uint256 totalSupply = ousd.totalSupply(); |
|
| 60 |
✓ 19.0M
|
uint256 totalBalance = getTotalBalance(); |
|
| 61 | |||
| 62 |
✓ 19.0M
|
Debugger.log("totalSupply", totalSupply);
|
|
| 63 |
✓ 19.0M
|
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 |
✓ 12.4M
|
uint256 nonRebasingSupply = ousd.nonRebasingSupply(); |
|
| 80 |
✓ 12.4M
|
uint256 totalSupply = ousd.totalSupply(); |
|
| 81 | |||
| 82 |
✓ 12.4M
|
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 |
✓ 19.5M
|
uint256 curRebasingCreditsPerToken = ousd |
|
| 95 |
.rebasingCreditsPerTokenHighres(); |
||
| 96 | |||
| 97 |
✓ 19.5M
|
Debugger.log( |
|
| 98 |
"prevRebasingCreditsPerToken", |
||
| 99 |
✓ 19.5M
|
prevRebasingCreditsPerToken |
|
| 100 |
); |
||
| 101 |
✓ 19.5M
|
Debugger.log("curRebasingCreditsPerToken", curRebasingCreditsPerToken);
|
|
| 102 | |||
| 103 |
✓ 19.5M
|
assert(curRebasingCreditsPerToken <= prevRebasingCreditsPerToken); |
|
| 104 | |||
| 105 |
✓ 19.2M
|
prevRebasingCreditsPerToken = curRebasingCreditsPerToken; |
|
| 106 |
} |
||
| 107 | |||
| 108 |
/** |
||
| 109 |
* @notice The rebasing credits per token ratio must greater than zero |
||
| 110 |
*/ |
||
| 111 |
function testRebasingCreditsPerTokenAboveZero() public {
|
||
| 112 |
✓ 14.3M
|
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 |
✓ 9.4M
|
uint256 totalNonRebasingSupply = ousd.nonRebasingSupply(); |
|
| 135 |
✓ 9.4M
|
uint256 totalNonRebasingBalance = getTotalNonRebasingBalance(); |
|
| 136 | |||
| 137 |
✓ 9.4M
|
Debugger.log("totalNonRebasingSupply", totalNonRebasingSupply);
|
|
| 138 |
✓ 9.4M
|
Debugger.log("totalNonRebasingBalance", totalNonRebasingBalance);
|
|
| 139 | |||
| 140 |
✓ 28.4M
|
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 |
✓ 21.0M
|
address target = getAccount(targetAcc); |
|
| 149 | |||
| 150 |
✓ 21.0M
|
(uint256 credits, uint256 creditsPerToken, ) = ousd |
|
| 151 |
.creditsBalanceOfHighres(target); |
||
| 152 |
✓ 21.0M
|
uint256 expectedBalance = credits.divPrecisely(creditsPerToken); |
|
| 153 | |||
| 154 |
✓ 21.0M
|
uint256 balance = ousd.balanceOf(target); |
|
| 155 | |||
| 156 |
✓ 21.0M
|
Debugger.log("credits", credits);
|
|
| 157 |
✓ 21.0M
|
Debugger.log("creditsPerToken", creditsPerToken);
|
|
| 158 |
✓ 21.0M
|
Debugger.log("expectedBalance", expectedBalance);
|
|
| 159 |
✓ 21.0M
|
Debugger.log("balance", balance);
|
|
| 160 | |||
| 161 |
✓ 21.0M
|
assert(expectedBalance == balance); |
|
| 162 |
} |
||
| 163 |
} |
||
| 164 |
96.8%
contracts/contracts/echidna/EchidnaTestTransfer.sol
Lines covered: 91 / 94 (96.8%)
| 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 |
✓ 14.0M
|
address from = getAccount(fromAcc); |
|
| 34 |
✓ 14.0M
|
address to = getAccount(toAcc); |
|
| 35 | |||
| 36 |
✓ 14.0M
|
require(from != to); |
|
| 37 | |||
| 38 |
✓ 11.2M
|
uint256 toBalBefore = ousd.balanceOf(to); |
|
| 39 |
✓ 11.2M
|
transfer(fromAcc, toAcc, amount); |
|
| 40 |
✓ 621.0K
|
uint256 toBalAfter = ousd.balanceOf(to); |
|
| 41 | |||
| 42 |
✓ 621.0K
|
Debugger.log("totalSupply", ousd.totalSupply());
|
|
| 43 |
✓ 621.0K
|
Debugger.log("toBalBefore", toBalBefore);
|
|
| 44 |
✓ 621.0K
|
Debugger.log("toBalAfter", toBalAfter);
|
|
| 45 | |||
| 46 |
✓ 621.0K
|
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 |
✓ 12.2M
|
address from = getAccount(fromAcc); |
|
| 62 |
✓ 12.2M
|
address to = getAccount(toAcc); |
|
| 63 | |||
| 64 |
✓ 12.2M
|
require(from != to); |
|
| 65 | |||
| 66 |
✓ 9.6M
|
uint256 toBalBefore = ousd.balanceOf(to); |
|
| 67 |
✓ 9.6M
|
transfer(fromAcc, toAcc, amount); |
|
| 68 |
✓ 681.9K
|
uint256 toBalAfter = ousd.balanceOf(to); |
|
| 69 | |||
| 70 |
✓ 681.9K
|
Debugger.log("totalSupply", ousd.totalSupply());
|
|
| 71 |
✓ 681.9K
|
Debugger.log("toBalBefore", toBalBefore);
|
|
| 72 |
✓ 681.9K
|
Debugger.log("toBalAfter", toBalAfter);
|
|
| 73 | |||
| 74 |
✓ 681.9K
|
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 |
✓ 17.3M
|
address from = getAccount(fromAcc); |
|
| 99 |
✓ 17.3M
|
address to = getAccount(toAcc); |
|
| 100 | |||
| 101 |
✓ 17.3M
|
require(from != to); |
|
| 102 | |||
| 103 |
✓ 14.5M
|
uint256 fromBalBefore = ousd.balanceOf(from); |
|
| 104 |
✓ 14.5M
|
transfer(fromAcc, toAcc, amount); |
|
| 105 |
✓ 664.0K
|
uint256 fromBalAfter = ousd.balanceOf(from); |
|
| 106 | |||
| 107 |
✓ 664.0K
|
Debugger.log("totalSupply", ousd.totalSupply());
|
|
| 108 |
✓ 664.0K
|
Debugger.log("fromBalBefore", fromBalBefore);
|
|
| 109 |
✓ 664.0K
|
Debugger.log("fromBalAfter", fromBalAfter);
|
|
| 110 | |||
| 111 |
✓ 1.3M
|
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 |
✓ 14.3M
|
address from = getAccount(fromAcc); |
|
| 127 |
✓ 14.3M
|
address to = getAccount(toAcc); |
|
| 128 | |||
| 129 |
✓ 14.3M
|
require(from != to); |
|
| 130 | |||
| 131 |
✓ 11.3M
|
uint256 fromBalBefore = ousd.balanceOf(from); |
|
| 132 |
✓ 11.3M
|
transfer(fromAcc, toAcc, amount); |
|
| 133 |
✓ 677.4K
|
uint256 fromBalAfter = ousd.balanceOf(from); |
|
| 134 | |||
| 135 |
✓ 677.4K
|
Debugger.log("totalSupply", ousd.totalSupply());
|
|
| 136 |
✓ 677.4K
|
Debugger.log("fromBalBefore", fromBalBefore);
|
|
| 137 |
✓ 677.4K
|
Debugger.log("fromBalAfter", fromBalAfter);
|
|
| 138 | |||
| 139 |
✓ 1.3M
|
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 |
✓ 16.9M
|
address from = getAccount(fromAcc); |
|
| 155 |
✓ 16.9M
|
address to = getAccount(toAcc); |
|
| 156 | |||
| 157 |
✓ 16.9M
|
require(from != to); |
|
| 158 | |||
| 159 |
✓ 14.3M
|
uint256 toBalBefore = ousd.balanceOf(to); |
|
| 160 |
✓ 14.3M
|
transfer(fromAcc, toAcc, amount); |
|
| 161 |
✓ 3.4M
|
uint256 toBalAfter = ousd.balanceOf(to); |
|
| 162 | |||
| 163 |
✓ 3.4M
|
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 |
✓ 3.4M
|
int256 error = int256(amount) - toDelta; |
|
| 169 | |||
| 170 |
✓ 3.4M
|
Debugger.log("totalSupply", ousd.totalSupply());
|
|
| 171 |
✓ 3.4M
|
Debugger.log("toBalBefore", toBalBefore);
|
|
| 172 |
✓ 3.4M
|
Debugger.log("toBalAfter", toBalAfter);
|
|
| 173 |
✓ 3.4M
|
Debugger.log("toDelta", toDelta);
|
|
| 174 |
✓ 4.0M
|
Debugger.log("error", error);
|
|
| 175 | |||
| 176 |
✓ 4.0M
|
assert(error >= 0); |
|
| 177 |
✓ 4.0M
|
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 |
✓ 15.3M
|
address from = getAccount(fromAcc); |
|
| 193 |
✓ 15.3M
|
address to = getAccount(toAcc); |
|
| 194 | |||
| 195 |
✓ 15.3M
|
require(from != to); |
|
| 196 | |||
| 197 |
✓ 9.7M
|
uint256 fromBalBefore = ousd.balanceOf(from); |
|
| 198 |
✓ 9.7M
|
transfer(fromAcc, toAcc, amount); |
|
| 199 |
✓ 639.4K
|
uint256 fromBalAfter = ousd.balanceOf(from); |
|
| 200 | |||
| 201 |
✓ 639.4K
|
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 |
✓ 639.4K
|
int256 error = int256(amount) + fromDelta; |
|
| 207 | |||
| 208 |
✓ 639.4K
|
Debugger.log("totalSupply", ousd.totalSupply());
|
|
| 209 |
✓ 639.4K
|
Debugger.log("fromBalBefore", fromBalBefore);
|
|
| 210 |
✓ 639.4K
|
Debugger.log("fromBalAfter", fromBalAfter);
|
|
| 211 |
✓ 639.4K
|
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 |
✓ 15.7M
|
address from = getAccount(fromAcc); |
|
| 241 |
✓ 15.7M
|
address to = getAccount(toAcc); |
|
| 242 | |||
| 243 |
✓ 15.7M
|
require(amount > 0); |
|
| 244 |
✓ 15.4M
|
amount = amount % ousd.balanceOf(from); |
|
| 245 | |||
| 246 |
✓ 11.8M
|
Debugger.log("Total supply", ousd.totalSupply());
|
|
| 247 | |||
| 248 |
✓ 5.2M
|
hevm.prank(from); |
|
| 249 |
// slither-disable-next-line unchecked-transfer |
||
| 250 |
✓ 5.2M
|
try ousd.transfer(to, amount) {
|
|
| 251 |
✓ 182.6M
|
assert(true); |
|
| 252 |
} catch {
|
||
| 253 |
✓ 19.5M
|
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 |
✓ 20.1M
|
address from = getAccount(fromAcc); |
|
| 270 |
✓ 20.1M
|
address to = getAccount(toAcc); |
|
| 271 | |||
| 272 |
✓ 20.1M
|
amount = ousd.balanceOf(from) + 1 + amount; |
|
| 273 | |||
| 274 |
✓ 19.5M
|
hevm.prank(from); |
|
| 275 |
// slither-disable-next-line unchecked-transfer |
||
| 276 |
✓ 19.5M
|
try ousd.transfer(to, amount) {
|
|
| 277 |
assert(false); |
||
| 278 |
} catch {
|
||
| 279 |
✓ 19.5M
|
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 |
✓ 14.4M
|
address target = getAccount(targetAcc); |
|
| 290 | |||
| 291 |
✓ 14.4M
|
uint256 balanceBefore = ousd.balanceOf(target); |
|
| 292 |
✓ 14.4M
|
transfer(targetAcc, targetAcc, amount); |
|
| 293 |
✓ 916.1K
|
uint256 balanceAfter = ousd.balanceOf(target); |
|
| 294 | |||
| 295 |
✓ 916.1K
|
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 |
✓ 17.9M
|
address from = getAccount(fromAcc); |
|
| 305 | |||
| 306 |
✓ 17.9M
|
hevm.prank(from); |
|
| 307 |
// slither-disable-next-line unchecked-transfer |
||
| 308 |
✓ 17.9M
|
try ousd.transfer(address(0), amount) {
|
|
| 309 |
assert(false); |
||
| 310 |
} catch {
|
||
| 311 |
✓ 17.9M
|
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: 4 / 4 (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 |
✓ 1
|
_setGovernor(msg.sender); |
|
| 13 |
} |
||
| 14 | |||
| 15 |
function _isNonRebasingAccountEchidna(address _account) |
||
| 16 |
public |
||
| 17 |
✓ 236.7M
|
returns (bool) |
|
| 18 |
{
|
||
| 19 |
✓ 236.7M
|
_autoMigrate(_account); |
|
| 20 |
✓ 236.7M
|
return alternativeCreditsPerToken[_account] > 0; |
|
| 21 |
} |
||
| 22 |
} |
||
| 23 |
35.3%
contracts/contracts/governance/Governable.sol
Lines covered: 6 / 17 (35.3%)
| 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 |
✓ 1
|
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 |
✓ 1
|
require(isGovernor(), "Caller is not the Governor"); |
|
| 77 |
_; |
||
| 78 |
} |
||
| 79 | |||
| 80 |
/** |
||
| 81 |
* @notice Returns true if the caller is the current Governor. |
||
| 82 |
*/ |
||
| 83 |
✓ 1
|
function isGovernor() public view returns (bool) {
|
|
| 84 |
✓ 1
|
return msg.sender == _governor(); |
|
| 85 |
} |
||
| 86 | |||
| 87 |
function _setGovernor(address newGovernor) internal {
|
||
| 88 |
✓ 1
|
emit GovernorshipTransferred(_governor(), newGovernor); |
|
| 89 | |||
| 90 |
bytes32 position = governorPosition; |
||
| 91 |
// solhint-disable-next-line no-inline-assembly |
||
| 92 |
assembly {
|
||
| 93 |
✓ 1
|
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 |
60.6%
contracts/contracts/token/OUSD.sol
Lines covered: 140 / 231 (60.6%)
| 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 |
✓ 153.3M
|
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 |
✓ 21.8M
|
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 |
✓ 1
|
require(_vaultAddress != address(0), "Zero vault address"); |
|
| 118 |
✓ 1
|
require(vaultAddress == address(0), "Already initialized"); |
|
| 119 | |||
| 120 |
✓ 1
|
rebasingCreditsPerToken_ = _initialCreditsPerToken; |
|
| 121 |
✓ 1
|
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 |
✓ 79.9M
|
require(vaultAddress == msg.sender, "Caller is not the Vault"); |
|
| 145 |
✓ 1.4B
|
_; |
|
| 146 |
} |
||
| 147 | |||
| 148 |
/** |
||
| 149 |
* @return High resolution rebasingCreditsPerToken |
||
| 150 |
*/ |
||
| 151 |
function rebasingCreditsPerTokenHighres() external view returns (uint256) {
|
||
| 152 |
✓ 33.8M
|
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 |
✓ 981.4M
|
function balanceOf(address _account) public view returns (uint256) {
|
|
| 183 |
✓ 981.4M
|
RebaseOptions state = rebaseState[_account]; |
|
| 184 |
✓ 981.4M
|
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 |
✓ 981.4M
|
uint256 baseBalance = (creditBalances[_account] * 1e18) / |
|
| 190 |
✓ 981.4M
|
_creditsPerToken(_account); |
|
| 191 |
✓ 981.4M
|
if (state == RebaseOptions.YieldDelegationTarget) {
|
|
| 192 |
// creditBalances of yieldFrom accounts equals token balances |
||
| 193 |
return baseBalance - creditBalances[yieldFrom[_account]]; |
||
| 194 |
} |
||
| 195 |
✓ 1.9B
|
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 |
✓ 21.0M
|
uint256, |
|
| 235 |
uint256, |
||
| 236 |
bool |
||
| 237 |
) |
||
| 238 |
{
|
||
| 239 |
return ( |
||
| 240 |
✓ 21.0M
|
creditBalances[_account], |
|
| 241 |
✓ 21.0M
|
_creditsPerToken(_account), |
|
| 242 |
✓ 21.0M
|
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 |
✓ 152.9M
|
function transfer(address _to, uint256 _value) external returns (bool) {
|
|
| 262 |
✓ 152.9M
|
require(_to != address(0), "Transfer to zero address"); |
|
| 263 | |||
| 264 |
✓ 135.0M
|
_executeTransfer(msg.sender, _to, _value); |
|
| 265 | |||
| 266 |
✓ 12.6M
|
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 |
✓ 29.4M
|
) external returns (bool) {
|
|
| 282 |
✓ 29.4M
|
require(_to != address(0), "Transfer to zero address"); |
|
| 283 |
✓ 29.4M
|
uint256 userAllowance = allowances[_from][msg.sender]; |
|
| 284 |
✓ 29.4M
|
require(_value <= userAllowance, "Allowance exceeded"); |
|
| 285 | |||
| 286 |
unchecked {
|
||
| 287 |
✓ 4.8M
|
allowances[_from][msg.sender] = userAllowance - _value; |
|
| 288 |
} |
||
| 289 | |||
| 290 |
✓ 4.8M
|
_executeTransfer(_from, _to, _value); |
|
| 291 | |||
| 292 |
✓ 3.8M
|
emit Transfer(_from, _to, _value); |
|
| 293 |
✓ 3.8M
|
return true; |
|
| 294 |
} |
||
| 295 | |||
| 296 |
function _executeTransfer( |
||
| 297 |
address _from, |
||
| 298 |
address _to, |
||
| 299 |
uint256 _value |
||
| 300 |
) internal {
|
||
| 301 |
( |
||
| 302 |
✓ 139.9M
|
int256 fromRebasingCreditsDiff, |
|
| 303 |
✓ 139.9M
|
int256 fromNonRebasingSupplyDiff |
|
| 304 |
✓ 139.9M
|
) = _adjustAccount(_from, -_value.toInt256()); |
|
| 305 |
( |
||
| 306 |
✓ 17.4M
|
int256 toRebasingCreditsDiff, |
|
| 307 |
✓ 17.4M
|
int256 toNonRebasingSupplyDiff |
|
| 308 |
✓ 17.4M
|
) = _adjustAccount(_to, _value.toInt256()); |
|
| 309 | |||
| 310 |
_adjustGlobals( |
||
| 311 |
✓ 16.3M
|
fromRebasingCreditsDiff + toRebasingCreditsDiff, |
|
| 312 |
✓ 16.3M
|
fromNonRebasingSupplyDiff + toNonRebasingSupplyDiff |
|
| 313 |
); |
||
| 314 |
} |
||
| 315 | |||
| 316 |
function _adjustAccount(address _account, int256 _balanceChange) |
||
| 317 |
internal |
||
| 318 |
✓ 185.9M
|
returns (int256 rebasingCreditsDiff, int256 nonRebasingSupplyDiff) |
|
| 319 |
{
|
||
| 320 |
✓ 185.9M
|
RebaseOptions state = rebaseState[_account]; |
|
| 321 |
✓ 185.9M
|
int256 currentBalance = balanceOf(_account).toInt256(); |
|
| 322 |
✓ 185.9M
|
if (currentBalance + _balanceChange < 0) {
|
|
| 323 |
✓ 74.0M
|
revert("Transfer amount exceeds balance");
|
|
| 324 |
} |
||
| 325 |
✓ 111.9M
|
uint256 newBalance = (currentBalance + _balanceChange).toUint256(); |
|
| 326 | |||
| 327 |
✓ 111.9M
|
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 |
✓ 111.9M
|
} 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 |
✓ 111.9M
|
_autoMigrate(_account); |
|
| 349 |
✓ 111.9M
|
uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[ |
|
| 350 |
_account |
||
| 351 |
]; |
||
| 352 |
✓ 111.9M
|
if (alternativeCreditsPerTokenMem > 0) {
|
|
| 353 |
✓ 48.8M
|
nonRebasingSupplyDiff = _balanceChange; |
|
| 354 |
✓ 48.8M
|
if (alternativeCreditsPerTokenMem != 1e18) {
|
|
| 355 |
alternativeCreditsPerToken[_account] = 1e18; |
||
| 356 |
} |
||
| 357 |
✓ 48.8M
|
creditBalances[_account] = newBalance; |
|
| 358 |
} else {
|
||
| 359 |
✓ 63.1M
|
uint256 newCredits = _balanceToRebasingCredits(newBalance); |
|
| 360 |
rebasingCreditsDiff = |
||
| 361 |
✓ 61.7M
|
newCredits.toInt256() - |
|
| 362 |
✓ 61.7M
|
creditBalances[_account].toInt256(); |
|
| 363 |
✓ 61.7M
|
creditBalances[_account] = newCredits; |
|
| 364 |
} |
||
| 365 |
} |
||
| 366 |
} |
||
| 367 | |||
| 368 |
function _adjustGlobals( |
||
| 369 |
int256 _rebasingCreditsDiff, |
||
| 370 |
int256 _nonRebasingSupplyDiff |
||
| 371 |
) internal {
|
||
| 372 |
✓ 159.3M
|
if (_rebasingCreditsDiff != 0) {
|
|
| 373 |
✓ 52.7M
|
rebasingCredits_ = (rebasingCredits_.toInt256() + |
|
| 374 |
✓ 52.7M
|
_rebasingCreditsDiff).toUint256(); |
|
| 375 |
} |
||
| 376 |
✓ 159.3M
|
if (_nonRebasingSupplyDiff != 0) {
|
|
| 377 |
✓ 45.6M
|
nonRebasingSupply = (nonRebasingSupply.toInt256() + |
|
| 378 |
✓ 45.6M
|
_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 |
✓ 24.1M
|
returns (uint256) |
|
| 393 |
{
|
||
| 394 |
✓ 24.1M
|
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 |
✓ 31.8M
|
function approve(address _spender, uint256 _value) external returns (bool) {
|
|
| 405 |
✓ 31.8M
|
allowances[msg.sender][_spender] = _value; |
|
| 406 |
✓ 44.4M
|
emit Approval(msg.sender, _spender, _value); |
|
| 407 |
✓ 44.4M
|
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 |
✓ 64.8M
|
require(_account != address(0), "Mint to the zero address"); |
|
| 416 | |||
| 417 |
// Account |
||
| 418 |
( |
||
| 419 |
✓ 64.8M
|
int256 toRebasingCreditsDiff, |
|
| 420 |
✓ 64.8M
|
int256 toNonRebasingSupplyDiff |
|
| 421 |
✓ 185.9M
|
) = _adjustAccount(_account, _amount.toInt256()); |
|
| 422 |
// Globals |
||
| 423 |
✓ 64.5M
|
_adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff); |
|
| 424 |
✓ 64.5M
|
totalSupply = totalSupply + _amount; |
|
| 425 | |||
| 426 |
✓ 64.5M
|
require(totalSupply < MAX_SUPPLY, "Max supply"); |
|
| 427 |
✓ 64.2M
|
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 |
✓ 79.9M
|
require(_account != address(0), "Burn from the zero address"); |
|
| 436 |
if (_amount == 0) {
|
||
| 437 |
✓ 79.9M
|
return; |
|
| 438 |
} |
||
| 439 | |||
| 440 |
// Account |
||
| 441 |
( |
||
| 442 |
✓ 41.1M
|
int256 toRebasingCreditsDiff, |
|
| 443 |
✓ 41.1M
|
int256 toNonRebasingSupplyDiff |
|
| 444 |
✓ 41.1M
|
) = _adjustAccount(_account, -_amount.toInt256()); |
|
| 445 |
// Globals |
||
| 446 |
✓ 12.2M
|
_adjustGlobals(toRebasingCreditsDiff, toNonRebasingSupplyDiff); |
|
| 447 |
✓ 12.2M
|
totalSupply = totalSupply - _amount; |
|
| 448 | |||
| 449 |
✓ 12.2M
|
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 |
✓ 1.0B
|
returns (uint256) |
|
| 461 |
{
|
||
| 462 |
✓ 1.0B
|
uint256 alternativeCreditsPerTokenMem = alternativeCreditsPerToken[ |
|
| 463 |
_account |
||
| 464 |
]; |
||
| 465 |
✓ 1.0B
|
if (alternativeCreditsPerTokenMem != 0) {
|
|
| 466 |
✓ 439.0M
|
return alternativeCreditsPerTokenMem; |
|
| 467 |
} else {
|
||
| 468 |
✓ 563.4M
|
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 |
✓ 348.6M
|
uint256 codeLen = _account.code.length; |
|
| 479 |
✓ 348.6M
|
bool isEOA = (codeLen == 0) || |
|
| 480 |
✓ 149.5M
|
(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 |
✓ 348.6M
|
(!isEOA) && |
|
| 486 |
✓ 149.5M
|
rebaseState[_account] == RebaseOptions.NotSet && |
|
| 487 |
✓ 5.3M
|
alternativeCreditsPerToken[_account] == 0 |
|
| 488 |
) {
|
||
| 489 |
✓ 5.3M
|
_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 |
✓ 96.2M
|
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 |
✓ 96.2M
|
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 |
✓ 61.0M
|
_rebaseOptIn(msg.sender); |
|
| 527 |
} |
||
| 528 | |||
| 529 |
function _rebaseOptIn(address _account) internal {
|
||
| 530 |
✓ 61.0M
|
uint256 balance = balanceOf(_account); |
|
| 531 | |||
| 532 |
// prettier-ignore |
||
| 533 |
require( |
||
| 534 |
✓ 61.0M
|
alternativeCreditsPerToken[_account] > 0 || |
|
| 535 |
// Accounts may explicitly `rebaseOptIn` regardless of |
||
| 536 |
// accounting if they have a 0 balance. |
||
| 537 |
✓ 40.0M
|
creditBalances[_account] == 0 |
|
| 538 |
, |
||
| 539 |
"Account must be non-rebasing" |
||
| 540 |
); |
||
| 541 |
✓ 52.4M
|
RebaseOptions state = rebaseState[_account]; |
|
| 542 |
// prettier-ignore |
||
| 543 |
require( |
||
| 544 |
✓ 52.4M
|
state == RebaseOptions.StdNonRebasing || |
|
| 545 |
✓ 31.4M
|
state == RebaseOptions.NotSet, |
|
| 546 |
"Only standard non-rebasing accounts can opt in" |
||
| 547 |
); |
||
| 548 | |||
| 549 |
✓ 33.0M
|
uint256 newCredits = _balanceToRebasingCredits(balance); |
|
| 550 | |||
| 551 |
// Account |
||
| 552 |
✓ 33.0M
|
rebaseState[_account] = RebaseOptions.StdRebasing; |
|
| 553 |
✓ 33.0M
|
alternativeCreditsPerToken[_account] = 0; |
|
| 554 |
✓ 33.0M
|
creditBalances[_account] = newCredits; |
|
| 555 |
// Globals |
||
| 556 |
✓ 33.0M
|
_adjustGlobals(newCredits.toInt256(), -balance.toInt256()); |
|
| 557 | |||
| 558 |
✓ 33.0M
|
emit AccountRebasingEnabled(_account); |
|
| 559 |
} |
||
| 560 | |||
| 561 |
/** |
||
| 562 |
* @notice The calling account will no longer receive yield |
||
| 563 |
*/ |
||
| 564 |
function rebaseOptOut() external {
|
||
| 565 |
✓ 43.5M
|
_rebaseOptOut(msg.sender); |
|
| 566 |
} |
||
| 567 | |||
| 568 |
function _rebaseOptOut(address _account) internal {
|
||
| 569 |
require( |
||
| 570 |
✓ 48.8M
|
alternativeCreditsPerToken[_account] == 0, |
|
| 571 |
"Account must be rebasing" |
||
| 572 |
); |
||
| 573 |
✓ 33.1M
|
RebaseOptions state = rebaseState[_account]; |
|
| 574 |
require( |
||
| 575 |
✓ 33.1M
|
state == RebaseOptions.StdRebasing || state == RebaseOptions.NotSet, |
|
| 576 |
"Only standard rebasing accounts can opt out" |
||
| 577 |
); |
||
| 578 | |||
| 579 |
✓ 33.1M
|
uint256 oldCredits = creditBalances[_account]; |
|
| 580 |
✓ 33.1M
|
uint256 balance = balanceOf(_account); |
|
| 581 | |||
| 582 |
// Account |
||
| 583 |
✓ 33.1M
|
rebaseState[_account] = RebaseOptions.StdNonRebasing; |
|
| 584 |
✓ 33.1M
|
alternativeCreditsPerToken[_account] = 1e18; |
|
| 585 |
✓ 33.1M
|
creditBalances[_account] = balance; |
|
| 586 |
// Globals |
||
| 587 |
✓ 33.1M
|
_adjustGlobals(-oldCredits.toInt256(), balance.toInt256()); |
|
| 588 | |||
| 589 |
✓ 66.2M
|
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 |
✓ 44.8M
|
require(totalSupply > 0, "Cannot increase 0 supply"); |
|
| 599 | |||
| 600 |
✓ 44.8M
|
if (totalSupply == _newTotalSupply) {
|
|
| 601 |
emit TotalSupplyUpdatedHighres( |
||
| 602 |
✓ 434.7K
|
totalSupply, |
|
| 603 |
✓ 434.7K
|
rebasingCredits_, |
|
| 604 |
✓ 434.7K
|
rebasingCreditsPerToken_ |
|
| 605 |
); |
||
| 606 |
return; |
||
| 607 |
} |
||
| 608 | |||
| 609 |
✓ 44.3M
|
totalSupply = _newTotalSupply > MAX_SUPPLY |
|
| 610 |
? MAX_SUPPLY |
||
| 611 |
✓ 25.2M
|
: _newTotalSupply; |
|
| 612 | |||
| 613 |
✓ 44.3M
|
uint256 rebasingSupply = totalSupply - nonRebasingSupply; |
|
| 614 |
// round up in the favour of the protocol |
||
| 615 |
✓ 36.5M
|
rebasingCreditsPerToken_ = |
|
| 616 |
✓ 39.0M
|
(rebasingCredits_ * 1e18 + rebasingSupply - 1) / |
|
| 617 |
✓ 39.0M
|
rebasingSupply; |
|
| 618 | |||
| 619 |
✓ 36.5M
|
require(rebasingCreditsPerToken_ > 0, "Invalid change in supply"); |
|
| 620 | |||
| 621 |
emit TotalSupplyUpdatedHighres( |
||
| 622 |
✓ 36.5M
|
totalSupply, |
|
| 623 |
✓ 36.5M
|
rebasingCredits_, |
|
| 624 |
✓ 36.5M
|
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 |
✓ 82.5M
|
_adjustGlobals(creditsChange, fromBalance.toInt256()); |
|
| 725 |
emit YieldUndelegated(_from, to); |
||
| 726 |
} |
||
| 727 |
} |
||
| 728 |
100.0%
contracts/contracts/utils/Helpers.sol
Lines covered: 1 / 1 (100.0%)
| 1 |
// SPDX-License-Identifier: BUSL-1.1 |
||
| 2 |
pragma solidity ^0.8.0; |
||
| 3 | |||
| 4 |
import { IBasicToken } from "../interfaces/IBasicToken.sol";
|
||
| 5 | |||
| 6 |
✓ 7
|
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 |
100.0%
contracts/contracts/utils/StableMath.sol
Lines covered: 5 / 5 (100.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 |
✓ 7
|
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 |
✓ 21.0M
|
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 |
✓ 21.0M
|
returns (uint256) |
|
| 109 |
{
|
||
| 110 |
// e.g. 8e18 * 1e18 = 8e36 |
||
| 111 |
✓ 21.0M
|
uint256 z = x.mul(FULL_SCALE); |
|
| 112 |
// e.g. 8e36 / 10e18 = 8e17 |
||
| 113 |
✓ 21.0M
|
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 |
100.0%
lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol
Lines covered: 1 / 1 (100.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 |
✓ 7
|
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 |
100.0%
lib/openzeppelin-contracts/contracts/utils/Address.sol
Lines covered: 1 / 1 (100.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 |
✓ 7
|
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 |
100.0%
lib/openzeppelin-contracts/contracts/utils/math/SafeCast.sol
Lines covered: 6 / 6 (100.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 |
✓ 7
|
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 |
✓ 210.3M
|
function toUint256(int256 value) internal pure returns (uint256) {
|
|
| 135 |
✓ 210.3M
|
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 |
✓ 803.2M
|
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 |
✓ 803.2M
|
require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); |
|
| 239 |
✓ 936.1M
|
return int256(value); |
|
| 240 |
} |
||
| 241 |
} |
||
| 242 |
100.0%
lib/openzeppelin-contracts/contracts/utils/math/SafeMath.sol
Lines covered: 5 / 5 (100.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 |
✓ 7
|
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 |
✓ 21.0M
|
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
|
|
| 122 |
✓ 21.0M
|
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 |
✓ 21.0M
|
function div(uint256 a, uint256 b) internal pure returns (uint256) {
|
|
| 136 |
✓ 21.0M
|
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 |
✓ 698.8M
|
contract CryticTester is Properties {}
|
|
| 8 |
57.1%
test/recon/Properties.sol
Lines covered: 4 / 7 (57.1%)
| 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 |
✓ 10.8M
|
if (!b) {
|
|
| 18 |
✓ 303.8K
|
emit AssertionFailed(reason); |
|
| 19 |
✓ 303.8K
|
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 |
✓ 10.8M
|
t(entropy > 0, ASSERTION_CANARY); |
|
| 30 |
} |
||
| 31 |
} |
||
| 32 |