diff -ruN access/AccessControl.sol access/AccessControl.sol --- access/AccessControl.sol 2022-09-20 11:01:10.429515094 +0200 +++ access/AccessControl.sol 2022-09-20 14:34:08.629602185 +0200 @@ -93,7 +93,7 @@ * * _Available since v4.6._ */ - function _checkRole(bytes32 role) internal view virtual { + function _checkRole(bytes32 role) public view virtual { // HARNESS: internal -> public _checkRole(role, _msgSender()); } diff -ruN access/Ownable.sol access/Ownable.sol --- access/Ownable.sol 2022-09-09 10:15:55.887175731 +0200 +++ access/Ownable.sol 2022-09-20 14:34:08.629602185 +0200 @@ -30,14 +30,6 @@ } /** - * @dev Throws if called by any account other than the owner. - */ - modifier onlyOwner() { - _checkOwner(); - _; - } - - /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { @@ -45,10 +37,11 @@ } /** - * @dev Throws if the sender is not the owner. + * @dev Throws if called by any account other than the owner. */ - function _checkOwner() internal view virtual { + modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); + _; } /** diff -ruN .gitignore .gitignore --- .gitignore 1970-01-01 01:00:00.000000000 +0100 +++ .gitignore 2022-09-20 14:34:08.626268788 +0200 @@ -0,0 +1,2 @@ +* +!.gitignore diff -ruN governance/extensions/GovernorCountingSimple.sol governance/extensions/GovernorCountingSimple.sol --- governance/extensions/GovernorCountingSimple.sol 2022-09-20 11:01:10.432848512 +0200 +++ governance/extensions/GovernorCountingSimple.sol 2022-09-20 14:34:08.632935582 +0200 @@ -27,7 +27,7 @@ mapping(address => bool) hasVoted; } - mapping(uint256 => ProposalVote) private _proposalVotes; + mapping(uint256 => ProposalVote) internal _proposalVotes; /** * @dev See {IGovernor-COUNTING_MODE}. diff -ruN governance/extensions/GovernorPreventLateQuorum.sol governance/extensions/GovernorPreventLateQuorum.sol --- governance/extensions/GovernorPreventLateQuorum.sol 2022-08-31 13:44:36.377724869 +0200 +++ governance/extensions/GovernorPreventLateQuorum.sol 2022-09-20 14:34:08.632935582 +0200 @@ -21,8 +21,8 @@ using SafeCast for uint256; using Timers for Timers.BlockNumber; - uint64 private _voteExtension; - mapping(uint256 => Timers.BlockNumber) private _extendedDeadlines; + uint64 internal _voteExtension; // PRIVATE => INTERNAL + mapping(uint256 => Timers.BlockNumber) internal _extendedDeadlines; // PRIVATE => INTERNAL /// @dev Emitted when a proposal deadline is pushed back due to reaching quorum late in its voting period. event ProposalExtended(uint256 indexed proposalId, uint64 extendedDeadline); diff -ruN governance/Governor.sol governance/Governor.sol --- governance/Governor.sol 2022-09-20 11:01:10.429515094 +0200 +++ governance/Governor.sol 2022-09-20 14:34:08.629602185 +0200 @@ -44,7 +44,7 @@ string private _name; - mapping(uint256 => ProposalCore) private _proposals; + mapping(uint256 => ProposalCore) internal _proposals; // This queue keeps track of the governor operating on itself. Calls to functions protected by the // {onlyGovernance} modifier needs to be whitelisted in this queue. Whitelisting is set in {_beforeExecute}, diff -ruN governance/TimelockController.sol governance/TimelockController.sol --- governance/TimelockController.sol 2022-09-09 10:15:55.887175731 +0200 +++ governance/TimelockController.sol 2022-09-20 14:34:08.629602185 +0200 @@ -28,10 +28,10 @@ bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE"); bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE"); bytes32 public constant CANCELLER_ROLE = keccak256("CANCELLER_ROLE"); - uint256 internal constant _DONE_TIMESTAMP = uint256(1); + uint256 public constant _DONE_TIMESTAMP = uint256(1); mapping(bytes32 => uint256) private _timestamps; - uint256 private _minDelay; + uint256 public _minDelay; /** * @dev Emitted when a call is scheduled as part of operation `id`. diff -ruN governance/utils/Votes.sol governance/utils/Votes.sol --- governance/utils/Votes.sol 2022-09-20 14:24:58.010074267 +0200 +++ governance/utils/Votes.sol 2022-09-20 14:34:08.632935582 +0200 @@ -35,7 +35,25 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); - mapping(address => address) private _delegation; + // HARNESS : Hooks cannot access any information from Checkpoints yet, so I am also updating votes and fromBlock in this struct + struct Ckpt { + uint32 fromBlock; + uint224 votes; + } + mapping(address => Ckpt) public _checkpoints; + + // HARNESSED getters + function numCheckpoints(address account) public view returns (uint32) { + return SafeCast.toUint32(_delegateCheckpoints[account]._checkpoints.length); + } + function ckptFromBlock(address account, uint32 pos) public view returns (uint32) { + return _delegateCheckpoints[account]._checkpoints[pos]._blockNumber; + } + function ckptVotes(address account, uint32 pos) public view returns (uint224) { + return _delegateCheckpoints[account]._checkpoints[pos]._value; + } + + mapping(address => address) public _delegation; mapping(address => Checkpoints.History) private _delegateCheckpoints; Checkpoints.History private _totalCheckpoints; @@ -124,7 +142,7 @@ * * Emits events {DelegateChanged} and {DelegateVotesChanged}. */ - function _delegate(address account, address delegatee) internal virtual { + function _delegate(address account, address delegatee) public virtual { address oldDelegate = delegates(account); _delegation[account] = delegatee; @@ -142,10 +160,10 @@ uint256 amount ) internal virtual { if (from == address(0)) { - _totalCheckpoints.push(_add, amount); + _totalCheckpoints.push(_totalCheckpoints.latest() + amount); // Harnessed to remove function pointers } if (to == address(0)) { - _totalCheckpoints.push(_subtract, amount); + _totalCheckpoints.push(_totalCheckpoints.latest() - amount); // Harnessed to remove function pointers } _moveDelegateVotes(delegates(from), delegates(to), amount); } @@ -160,11 +178,13 @@ ) private { if (from != to && amount > 0) { if (from != address(0)) { - (uint256 oldValue, uint256 newValue) = _delegateCheckpoints[from].push(_subtract, amount); + (uint256 oldValue, uint256 newValue) = _delegateCheckpoints[from].push(_delegateCheckpoints[from].latest() - amount); // HARNESSED TO REMOVE FUNCTION POINTERS + _checkpoints[from] = Ckpt({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newValue)}); // HARNESS emit DelegateVotesChanged(from, oldValue, newValue); } if (to != address(0)) { - (uint256 oldValue, uint256 newValue) = _delegateCheckpoints[to].push(_add, amount); + (uint256 oldValue, uint256 newValue) = _delegateCheckpoints[to].push(_delegateCheckpoints[to].latest() + amount); // HARNESSED TO REMOVE FUNCTION POINTERS + _checkpoints[to] = Ckpt({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newValue)}); // HARNESS emit DelegateVotesChanged(to, oldValue, newValue); } } @@ -207,5 +227,5 @@ /** * @dev Must return the voting units held by an account. */ - function _getVotingUnits(address) internal view virtual returns (uint256); + function _getVotingUnits(address) public virtual returns (uint256); // HARNESS: internal -> public } diff -ruN metatx/MinimalForwarder.sol metatx/MinimalForwarder.sol --- metatx/MinimalForwarder.sol 2022-09-20 11:16:48.456850883 +0200 +++ metatx/MinimalForwarder.sol 2022-09-20 14:34:08.632935582 +0200 @@ -8,11 +8,6 @@ /** * @dev Simple minimal forwarder to be used together with an ERC2771 compatible contract. See {ERC2771Context}. - * - * MinimalForwarder is mainly meant for testing, as it is missing features to be a good production-ready forwarder. This - * contract does not intend to have all the properties that are needed for a sound forwarding system. A fully - * functioning forwarding system with good properties requires more complexity. We suggest you look at other projects - * such as the GSN which do have the goal of building a system like that. */ contract MinimalForwarder is EIP712 { using ECDSA for bytes32; diff -ruN mocks/ERC20TokenizedVaultMock.sol mocks/ERC20TokenizedVaultMock.sol --- mocks/ERC20TokenizedVaultMock.sol 1970-01-01 01:00:00.000000000 +0100 +++ mocks/ERC20TokenizedVaultMock.sol 2022-09-20 14:34:08.632935582 +0200 @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../token/ERC20/extensions/ERC20TokenizedVault.sol"; + +// mock class using ERC20 +contract ERC20TokenizedVaultMock is ERC20TokenizedVault { + constructor( + IERC20Metadata asset, + string memory name, + string memory symbol + ) ERC20(name, symbol) ERC20TokenizedVault(asset) {} + + function mockMint(address account, uint256 amount) public { + _mint(account, amount); + } + + function mockBurn(address account, uint256 amount) public { + _burn(account, amount); + } +} diff -ruN mocks/MathMock.sol mocks/MathMock.sol --- mocks/MathMock.sol 2022-09-20 14:24:58.013407601 +0200 +++ mocks/MathMock.sol 2022-09-20 14:34:24.803248911 +0200 @@ -45,4 +45,8 @@ function log256(uint256 a, Math.Rounding direction) public pure returns (uint256) { return Math.log256(a, direction); } + + function sqrt(uint256 a, Math.Rounding direction) public pure returns (uint256) { + return Math.sqrt(a, direction); + } } diff -ruN mocks/SafeERC20Helper.sol mocks/SafeERC20Helper.sol --- mocks/SafeERC20Helper.sol 2022-09-20 14:24:58.013407601 +0200 +++ mocks/SafeERC20Helper.sol 2022-09-20 15:09:17.135329080 +0200 @@ -4,7 +4,6 @@ import "../utils/Context.sol"; import "../token/ERC20/IERC20.sol"; -import "../token/ERC20/extensions/draft-ERC20Permit.sol"; import "../token/ERC20/utils/SafeERC20.sol"; contract ERC20ReturnFalseMock is Context { @@ -106,42 +105,43 @@ } } -contract ERC20PermitNoRevertMock is - ERC20("ERC20PermitNoRevertMock", "ERC20PermitNoRevertMock"), - ERC20Permit("ERC20PermitNoRevertMock") -{ - function getChainId() external view returns (uint256) { - return block.chainid; - } - - function permitThatMayRevert( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) public { - super.permit(owner, spender, value, deadline, v, r, s); - } - - function permit( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) public override { - try this.permitThatMayRevert(owner, spender, value, deadline, v, r, s) { - // do nothing - } catch { - // do nothing - } - } -} +// Harness remove ? +// contract ERC20PermitNoRevertMock is +// ERC20("ERC20PermitNoRevertMock", "ERC20PermitNoRevertMock"), +// ERC20Permit("ERC20PermitNoRevertMock") +// { +// function getChainId() external view returns (uint256) { +// return block.chainid; +// } + +// function permitThatMayRevert( +// address owner, +// address spender, +// uint256 value, +// uint256 deadline, +// uint8 v, +// bytes32 r, +// bytes32 s +// ) public { +// super.permit(owner, spender, value, deadline, v, r, s); +// } + +// function permit( +// address owner, +// address spender, +// uint256 value, +// uint256 deadline, +// uint8 v, +// bytes32 r, +// bytes32 s +// ) public override { +// try this.permitThatMayRevert(owner, spender, value, deadline, v, r, s) { +// // do nothing +// } catch { +// // do nothing +// } +// } +// } contract SafeERC20Wrapper is Context { using SafeERC20 for IERC20; @@ -172,18 +172,6 @@ _token.safeDecreaseAllowance(address(0), amount); } - function permit( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) public { - SafeERC20.safePermit(IERC20Permit(address(_token)), owner, spender, value, deadline, v, r, s); - } - function setAllowance(uint256 allowance_) public { ERC20ReturnTrueMock(address(_token)).setAllowance(allowance_); } diff -ruN proxy/beacon/BeaconProxy.sol proxy/beacon/BeaconProxy.sol --- proxy/beacon/BeaconProxy.sol 2022-09-09 10:15:55.890509851 +0200 +++ proxy/beacon/BeaconProxy.sol 2022-09-20 14:34:24.806582310 +0200 @@ -28,6 +28,7 @@ * - `beacon` must be a contract with the interface {IBeacon}. */ constructor(address beacon, bytes memory data) payable { + assert(_BEACON_SLOT == bytes32(uint256(keccak256("eip1967.proxy.beacon")) - 1)); _upgradeBeaconToAndCall(beacon, data, false); } diff -ruN proxy/Clones.sol proxy/Clones.sol --- proxy/Clones.sol 2022-09-20 14:24:58.013407601 +0200 +++ proxy/Clones.sol 2022-09-20 14:59:00.690035663 +0200 @@ -27,10 +27,10 @@ assembly { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. - mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) + mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x602d8060093d393df3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) - instance := create(0, 0x09, 0x37) + instance := create(0, 0x0A, 0x36) } require(instance != address(0), "ERC1167: create failed"); } @@ -47,10 +47,10 @@ assembly { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. - mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) + mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x602d8060093d393df3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) - instance := create2(0, 0x09, 0x37, salt) + instance := create2(0, 0x0A, 0x36, salt) } require(instance != address(0), "ERC1167: create2 failed"); } @@ -66,13 +66,13 @@ /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) - mstore(add(ptr, 0x38), deployer) - mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff) - mstore(add(ptr, 0x14), implementation) - mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73) - mstore(add(ptr, 0x58), salt) - mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37)) - predicted := keccak256(add(ptr, 0x43), 0x55) + mstore(add(ptr, 0x37), deployer) + mstore(add(ptr, 0x23), 0x5af43d82803e903d91602b57fd5bf3ff) + mstore(add(ptr, 0x13), implementation) + mstore(ptr, 0x602d8060093d393df3363d3d373d3d3d363d73) + mstore(add(ptr, 0x57), salt) + mstore(add(ptr, 0x77), keccak256(add(ptr, 0x0b), 0x36)) + predicted := keccak256(add(ptr, 0x44), 0x55) } } diff -ruN proxy/ERC1967/ERC1967Proxy.sol proxy/ERC1967/ERC1967Proxy.sol --- proxy/ERC1967/ERC1967Proxy.sol 2022-09-09 10:15:55.890509851 +0200 +++ proxy/ERC1967/ERC1967Proxy.sol 2022-09-20 14:34:24.806582310 +0200 @@ -20,6 +20,7 @@ * function call, and allows initializing the storage of the proxy like a Solidity constructor. */ constructor(address _logic, bytes memory _data) payable { + assert(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)); _upgradeToAndCall(_logic, _data, false); } diff -ruN proxy/transparent/TransparentUpgradeableProxy.sol proxy/transparent/TransparentUpgradeableProxy.sol --- proxy/transparent/TransparentUpgradeableProxy.sol 2022-09-09 10:15:55.890509851 +0200 +++ proxy/transparent/TransparentUpgradeableProxy.sol 2022-09-20 14:34:24.806582310 +0200 @@ -36,6 +36,7 @@ address admin_, bytes memory _data ) payable ERC1967Proxy(_logic, _data) { + assert(_ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1)); _changeAdmin(admin_); } diff -ruN proxy/utils/Initializable.sol proxy/utils/Initializable.sol --- proxy/utils/Initializable.sol 2022-09-20 11:16:48.456850883 +0200 +++ proxy/utils/Initializable.sol 2022-09-20 14:34:24.806582310 +0200 @@ -59,12 +59,12 @@ * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ - uint8 private _initialized; + uint8 internal _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ - bool private _initializing; + bool internal _initializing; /** * @dev Triggered when the contract has been initialized or reinitialized. diff -ruN security/Pausable.sol security/Pausable.sol --- security/Pausable.sol 2022-09-09 10:15:55.890509851 +0200 +++ security/Pausable.sol 2022-09-20 14:34:24.809915708 +0200 @@ -35,6 +35,13 @@ } /** + * @dev Returns true if the contract is paused, and false otherwise. + */ + function paused() public view virtual returns (bool) { + return _paused; + } + + /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: @@ -42,7 +49,7 @@ * - The contract must not be paused. */ modifier whenNotPaused() { - _requireNotPaused(); + require(!paused(), "Pausable: paused"); _; } @@ -54,29 +61,8 @@ * - The contract must be paused. */ modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { require(paused(), "Pausable: not paused"); + _; } /** diff -ruN token/ERC1155/ERC1155.sol token/ERC1155/ERC1155.sol --- token/ERC1155/ERC1155.sol 2022-09-20 11:01:10.432848512 +0200 +++ token/ERC1155/ERC1155.sol 2022-09-20 14:34:24.809915708 +0200 @@ -21,7 +21,7 @@ using Address for address; // Mapping from token ID to account balances - mapping(uint256 => mapping(address => uint256)) private _balances; + mapping(uint256 => mapping(address => uint256)) internal _balances; // MUNGED private => internal // Mapping from account to operator approvals mapping(address => mapping(address => bool)) private _operatorApprovals; @@ -471,7 +471,7 @@ uint256 id, uint256 amount, bytes memory data - ) private { + ) public { // HARNESS: private -> public if (to.isContract()) { try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) { if (response != IERC1155Receiver.onERC1155Received.selector) { @@ -492,7 +492,7 @@ uint256[] memory ids, uint256[] memory amounts, bytes memory data - ) private { + ) public { // HARNESS: private -> public if (to.isContract()) { try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns ( bytes4 response diff -ruN token/ERC20/ERC20.sol token/ERC20/ERC20.sol --- token/ERC20/ERC20.sol 2022-09-20 13:34:47.024598756 +0200 +++ token/ERC20/ERC20.sol 2022-09-20 14:34:24.809915708 +0200 @@ -282,7 +282,7 @@ * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ - function _burn(address account, uint256 amount) internal virtual { + function _burn(address account, uint256 amount) public virtual { // HARNESS: internal -> public require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); diff -ruN token/ERC20/extensions/ERC20FlashMint.sol token/ERC20/extensions/ERC20FlashMint.sol --- token/ERC20/extensions/ERC20FlashMint.sol 2022-09-20 11:01:10.432848512 +0200 +++ token/ERC20/extensions/ERC20FlashMint.sol 2022-09-20 14:34:24.809915708 +0200 @@ -51,9 +51,11 @@ // silence warning about unused variable without the addition of bytecode. token; amount; - return 0; + return fee; // HARNESS: made "return" nonzero } + uint256 public fee; // HARNESS: added it to simulate random fee amount + /** * @dev Returns the receiver address of the flash fee. By default this * implementation returns the address(0) which means the fee amount will be burnt. diff -ruN token/ERC20/extensions/ERC20TokenizedVault.sol token/ERC20/extensions/ERC20TokenizedVault.sol --- token/ERC20/extensions/ERC20TokenizedVault.sol 1970-01-01 01:00:00.000000000 +0100 +++ token/ERC20/extensions/ERC20TokenizedVault.sol 2022-09-20 14:34:24.809915708 +0200 @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "../ERC20.sol"; +import "../utils/SafeERC20.sol"; +import "../../../interfaces/IERC4626.sol"; +import "../../../utils/math/Math.sol"; + +/** + * @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in + * https://eips.ethereum.org/EIPS/eip-4626[EIP-4626]. + * + * This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for + * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends + * the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this + * contract and not the "assets" token which is an independent contract. + * + * _Available since v4.7._ + */ +abstract contract ERC20TokenizedVault is ERC20, IERC4626 { + using Math for uint256; + + IERC20Metadata private immutable _asset; + + /** + * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777). + */ + constructor(IERC20Metadata asset_) { + _asset = asset_; + } + + /** @dev See {IERC4262-asset} */ + function asset() public view virtual override returns (address) { + return address(_asset); + } + + /** @dev See {IERC4262-totalAssets} */ + function totalAssets() public view virtual override returns (uint256) { + return _asset.balanceOf(address(this)); + } + + /** @dev See {IERC4262-convertToShares} */ + function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) { + return _convertToShares(assets, Math.Rounding.Down); + } + + /** @dev See {IERC4262-convertToAssets} */ + function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) { + return _convertToAssets(shares, Math.Rounding.Down); + } + + /** @dev See {IERC4262-maxDeposit} */ + function maxDeposit(address) public view virtual override returns (uint256) { + return _isVaultCollateralized() ? type(uint256).max : 0; + } + + /** @dev See {IERC4262-maxMint} */ + function maxMint(address) public view virtual override returns (uint256) { + return type(uint256).max; + } + + /** @dev See {IERC4262-maxWithdraw} */ + function maxWithdraw(address owner) public view virtual override returns (uint256) { + return _convertToAssets(balanceOf(owner), Math.Rounding.Down); + } + + /** @dev See {IERC4262-maxRedeem} */ + function maxRedeem(address owner) public view virtual override returns (uint256) { + return balanceOf(owner); + } + + /** @dev See {IERC4262-previewDeposit} */ + function previewDeposit(uint256 assets) public view virtual override returns (uint256) { + return _convertToShares(assets, Math.Rounding.Down); + } + + /** @dev See {IERC4262-previewMint} */ + function previewMint(uint256 shares) public view virtual override returns (uint256) { + return _convertToAssets(shares, Math.Rounding.Up); + } + + /** @dev See {IERC4262-previewWithdraw} */ + function previewWithdraw(uint256 assets) public view virtual override returns (uint256) { + return _convertToShares(assets, Math.Rounding.Up); + } + + /** @dev See {IERC4262-previewRedeem} */ + function previewRedeem(uint256 shares) public view virtual override returns (uint256) { + return _convertToAssets(shares, Math.Rounding.Down); + } + + /** @dev See {IERC4262-deposit} */ + function deposit(uint256 assets, address receiver) public virtual override returns (uint256) { + require(assets <= maxDeposit(receiver), "ERC20TokenizedVault: deposit more than max"); + + uint256 shares = previewDeposit(assets); + _deposit(_msgSender(), receiver, assets, shares); + + return shares; + } + + /** @dev See {IERC4262-mint} */ + function mint(uint256 shares, address receiver) public virtual override returns (uint256) { + require(shares <= maxMint(receiver), "ERC20TokenizedVault: mint more than max"); + + uint256 assets = previewMint(shares); + _deposit(_msgSender(), receiver, assets, shares); + + return assets; + } + + /** @dev See {IERC4262-withdraw} */ + function withdraw( + uint256 assets, + address receiver, + address owner + ) public virtual override returns (uint256) { + require(assets <= maxWithdraw(owner), "ERC20TokenizedVault: withdraw more than max"); + + uint256 shares = previewWithdraw(assets); + _withdraw(_msgSender(), receiver, owner, assets, shares); + + return shares; + } + + /** @dev See {IERC4262-redeem} */ + function redeem( + uint256 shares, + address receiver, + address owner + ) public virtual override returns (uint256) { + require(shares <= maxRedeem(owner), "ERC20TokenizedVault: redeem more than max"); + + uint256 assets = previewRedeem(shares); + _withdraw(_msgSender(), receiver, owner, assets, shares); + + return assets; + } + + /** + * @dev Internal convertion function (from assets to shares) with support for rounding direction + * + * Will revert if assets > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset + * would represent an infinite amout of shares. + */ + function _convertToShares(uint256 assets, Math.Rounding rounding) internal view virtual returns (uint256 shares) { + uint256 supply = totalSupply(); + return + (assets == 0 || supply == 0) + ? assets.mulDiv(10**decimals(), 10**_asset.decimals(), rounding) + : assets.mulDiv(supply, totalAssets(), rounding); + } + + /** + * @dev Internal convertion function (from shares to assets) with support for rounding direction + */ + function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256 assets) { + uint256 supply = totalSupply(); + return + (supply == 0) + ? shares.mulDiv(10**_asset.decimals(), 10**decimals(), rounding) + : shares.mulDiv(totalAssets(), supply, rounding); + } + + /** + * @dev Deposit/mint common workflow + */ + function _deposit( + address caller, + address receiver, + uint256 assets, + uint256 shares + ) private { + // If _asset is ERC777, `transferFrom` can trigger a reenterancy BEFORE the transfer happens through the + // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer, + // calls the vault, which is assumed not malicious. + // + // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the + // assets are transfered and before the shares are minted, which is a valid state. + // slither-disable-next-line reentrancy-no-eth + SafeERC20.safeTransferFrom(_asset, caller, address(this), assets); + _mint(receiver, shares); + + emit Deposit(caller, receiver, assets, shares); + } + + /** + * @dev Withdraw/redeem common workflow + */ + function _withdraw( + address caller, + address receiver, + address owner, + uint256 assets, + uint256 shares + ) private { + if (caller != owner) { + _spendAllowance(owner, caller, shares); + } + + // If _asset is ERC777, `transfer` can trigger trigger a reentrancy AFTER the transfer happens through the + // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer, + // calls the vault, which is assumed not malicious. + // + // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the + // shares are burned and after the assets are transfered, which is a valid state. + _burn(owner, shares); + SafeERC20.safeTransfer(_asset, receiver, assets); + + emit Withdraw(caller, receiver, owner, assets, shares); + } + + function _isVaultCollateralized() private view returns (bool) { + return totalAssets() > 0 || totalSupply() == 0; + } +} diff -ruN token/ERC20/extensions/ERC20Votes.sol token/ERC20/extensions/ERC20Votes.sol --- token/ERC20/extensions/ERC20Votes.sol 2022-09-20 14:24:58.016740934 +0200 +++ token/ERC20/extensions/ERC20Votes.sol 2022-09-20 15:05:11.770836991 +0200 @@ -33,8 +33,8 @@ bytes32 private constant _DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)"); - mapping(address => address) private _delegates; - mapping(address => Checkpoint[]) private _checkpoints; + mapping(address => address) public _delegates; + mapping(address => Checkpoint[]) public _checkpoints; Checkpoint[] private _totalSupplyCheckpoints; /** @@ -165,7 +165,7 @@ /** * @dev Maximum token supply. Defaults to `type(uint224).max` (2^224^ - 1). */ - function _maxSupply() internal view virtual returns (uint224) { + function _maxSupply() public view virtual returns (uint224) { //harnessed to public return type(uint224).max; } @@ -176,16 +176,16 @@ super._mint(account, amount); require(totalSupply() <= _maxSupply(), "ERC20Votes: total supply risks overflowing votes"); - _writeCheckpoint(_totalSupplyCheckpoints, _add, amount); + _writeCheckpointAdd(_totalSupplyCheckpoints, amount); // HARNESS: new version without pointer } /** * @dev Snapshots the totalSupply after it has been decreased. */ - function _burn(address account, uint256 amount) internal virtual override { + function _burn(address account, uint256 amount) public virtual override { // HARNESS: internal -> public (to comply with the ERC20 harness) super._burn(account, amount); - _writeCheckpoint(_totalSupplyCheckpoints, _subtract, amount); + _writeCheckpointSub(_totalSupplyCheckpoints, amount); // HARNESS: new version without pointer } /** @@ -208,7 +208,7 @@ * * Emits events {DelegateChanged} and {DelegateVotesChanged}. */ - function _delegate(address delegator, address delegatee) internal virtual { + function _delegate(address delegator, address delegatee) public virtual { // HARNESSED TO MAKE PUBLIC address currentDelegate = delegates(delegator); uint256 delegatorBalance = balanceOf(delegator); _delegates[delegator] = delegatee; @@ -225,12 +225,13 @@ ) private { if (src != dst && amount > 0) { if (src != address(0)) { - (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[src], _subtract, amount); + (uint256 oldWeight, uint256 newWeight) = _writeCheckpointSub(_checkpoints[src], amount); // HARNESS: new version without pointer + emit DelegateVotesChanged(src, oldWeight, newWeight); } if (dst != address(0)) { - (uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(_checkpoints[dst], _add, amount); + (uint256 oldWeight, uint256 newWeight) = _writeCheckpointAdd(_checkpoints[dst], amount); // HARNESS: new version without pointer emit DelegateVotesChanged(dst, oldWeight, newWeight); } } @@ -255,6 +256,55 @@ } } + // HARNESS: split _writeCheckpoint() to two functions as a workaround for function pointers that cannot be managed by the tool + function _writeCheckpointAdd( + Checkpoint[] storage ckpts, + uint256 delta + ) private returns (uint256 oldWeight, uint256 newWeight) { + uint256 pos = ckpts.length; + oldWeight = pos == 0 ? 0 : ckpts[pos - 1].votes; + newWeight = _add(oldWeight, delta); + + if (pos > 0 && ckpts[pos - 1].fromBlock == block.number) { + ckpts[pos - 1].votes = SafeCast.toUint224(newWeight); + } else { + ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)})); + } + } + + function _writeCheckpointSub( + Checkpoint[] storage ckpts, + uint256 delta + ) private returns (uint256 oldWeight, uint256 newWeight) { + uint256 pos = ckpts.length; + oldWeight = pos == 0 ? 0 : ckpts[pos - 1].votes; + newWeight = _subtract(oldWeight, delta); + + if (pos > 0 && ckpts[pos - 1].fromBlock == block.number) { + ckpts[pos - 1].votes = SafeCast.toUint224(newWeight); + } else { + ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)})); + } + } + + // backup of original function + // + // function _writeCheckpoint( + // Checkpoint[] storage ckpts, + // function(uint256, uint256) view returns (uint256) op, + // uint256 delta + // ) private returns (uint256 oldWeight, uint256 newWeight) { + // uint256 pos = ckpts.length; + // oldWeight = pos == 0 ? 0 : ckpts[pos - 1].votes; + // newWeight = op(oldWeight, delta); + // + // if (pos > 0 && ckpts[pos - 1].fromBlock == block.number) { + // ckpts[pos - 1].votes = SafeCast.toUint224(newWeight); + // } else { + // ckpts.push(Checkpoint({fromBlock: SafeCast.toUint32(block.number), votes: SafeCast.toUint224(newWeight)})); + // } + // } + function _add(uint256 a, uint256 b) private pure returns (uint256) { return a + b; } diff -ruN token/ERC20/extensions/ERC20Wrapper.sol token/ERC20/extensions/ERC20Wrapper.sol --- token/ERC20/extensions/ERC20Wrapper.sol 2022-08-31 13:44:36.381058287 +0200 +++ token/ERC20/extensions/ERC20Wrapper.sol 2022-09-20 14:34:24.809915708 +0200 @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/extensions/ERC20Wrapper.sol) +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/ERC20Wrapper.sol) pragma solidity ^0.8.0; @@ -23,17 +23,6 @@ } /** - * @dev See {ERC20-decimals}. - */ - function decimals() public view virtual override returns (uint8) { - try IERC20Metadata(address(underlying)).decimals() returns (uint8 value) { - return value; - } catch { - return super.decimals(); - } - } - - /** * @dev Allow a user to deposit underlying tokens and mint the corresponding number of wrapped tokens. */ function depositFor(address account, uint256 amount) public virtual returns (bool) { @@ -55,7 +44,7 @@ * @dev Mint wrapped token to cover any underlyingTokens that would have been transferred by mistake. Internal * function that can be exposed with access control if desired. */ - function _recover(address account) internal virtual returns (uint256) { + function _recover(address account) public virtual returns (uint256) { // HARNESS: internal -> public uint256 value = underlying.balanceOf(address(this)) - totalSupply(); _mint(account, value); return value; diff -ruN token/ERC20/README.adoc token/ERC20/README.adoc --- token/ERC20/README.adoc 2022-09-20 13:34:47.024598756 +0200 +++ token/ERC20/README.adoc 2022-09-20 14:34:24.809915708 +0200 @@ -24,7 +24,7 @@ * {ERC20Votes}: support for voting and vote delegation. * {ERC20VotesComp}: support for voting and vote delegation (compatible with Compound's token, with uint96 restrictions). * {ERC20Wrapper}: wrapper to create an ERC20 backed by another ERC20, with deposit and withdraw methods. Useful in conjunction with {ERC20Votes}. -* {ERC4626}: tokenized vault that manages shares (represented as ERC20) that are backed by assets (another ERC20). +* {ERC20TokenizedVault}: tokenized vault that manages shares (represented as ERC20) that are backed by assets (another ERC20). Finally, there are some utilities to interact with ERC20 contracts in various ways. @@ -63,7 +63,7 @@ {{ERC20FlashMint}} -{{ERC4626}} +{{ERC20TokenizedVault}} == Draft EIPs diff -ruN token/ERC20/utils/SafeERC20.sol token/ERC20/utils/SafeERC20.sol --- token/ERC20/utils/SafeERC20.sol 2022-09-09 10:15:55.893843970 +0200 +++ token/ERC20/utils/SafeERC20.sol 2022-09-20 14:34:28.259983206 +0200 @@ -4,7 +4,6 @@ pragma solidity ^0.8.0; import "../IERC20.sol"; -import "../extensions/draft-IERC20Permit.sol"; import "../../../utils/Address.sol"; /** @@ -80,22 +79,6 @@ } } - function safePermit( - IERC20Permit token, - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) internal { - uint256 nonceBefore = token.nonces(owner); - token.permit(owner, spender, value, deadline, v, r, s); - uint256 nonceAfter = token.nonces(owner); - require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); - } - /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). diff -ruN token/ERC721/extensions/draft-ERC721Votes.sol token/ERC721/extensions/draft-ERC721Votes.sol --- token/ERC721/extensions/draft-ERC721Votes.sol 2022-09-20 14:24:58.016740934 +0200 +++ token/ERC721/extensions/draft-ERC721Votes.sol 2022-09-20 14:34:28.259983206 +0200 @@ -49,7 +49,7 @@ /** * @dev Returns the balance of `account`. */ - function _getVotingUnits(address account) internal view virtual override returns (uint256) { + function _getVotingUnits(address account) public view virtual override returns (uint256) { return balanceOf(account); } } diff -ruN utils/Address.sol utils/Address.sol --- utils/Address.sol 2022-09-20 11:01:10.432848512 +0200 +++ utils/Address.sol 2022-09-20 14:34:28.259983206 +0200 @@ -131,6 +131,7 @@ uint256 value, string memory errorMessage ) internal returns (bytes memory) { + return ""; // external calls havoc require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage);