From 718657ba26fcf06a3a70870191b8243119766cf0 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Fri, 25 Mar 2022 13:30:39 -0400 Subject: [PATCH 01/13] Wrapped OUSD --- brownie/scripts/wrapped_ousd_test.py | 111 ++++++++ contracts/contracts/token/WrappedOusd.sol | 43 +++ contracts/deploy/001_core.js | 16 ++ contracts/deploy/039_wrapped_ousd.js | 47 ++++ contracts/lib/solmate/.dapprc | 14 + contracts/lib/solmate/.gas-snapshot | 215 +++++++++++++++ contracts/lib/solmate/.gitattributes | 3 + .../solmate/.github/pull_request_template.md | 14 + .../solmate/.github/workflows/dapp-tests.yml | 27 ++ .../solmate/.github/workflows/forge-tests.yml | 20 ++ contracts/lib/solmate/.gitignore | 3 + contracts/lib/solmate/.gitmodules | 3 + contracts/lib/solmate/.prettierrc | 14 + contracts/lib/solmate/.vscode/settings.json | 10 + contracts/lib/solmate/src/auth/Auth.sol | 64 +++++ .../auth/authorities/MultiRolesAuthority.sol | 123 +++++++++ .../src/auth/authorities/RolesAuthority.sol | 108 ++++++++ contracts/lib/solmate/src/mixins/ERC4626.sol | 183 +++++++++++++ contracts/lib/solmate/src/tokens/ERC1155.sol | 257 ++++++++++++++++++ contracts/lib/solmate/src/tokens/ERC20.sol | 206 ++++++++++++++ contracts/lib/solmate/src/tokens/ERC721.sol | 216 +++++++++++++++ contracts/lib/solmate/src/tokens/WETH.sol | 35 +++ .../solmate/src/utils/Bytes32AddressLib.sol | 14 + contracts/lib/solmate/src/utils/CREATE3.sol | 82 ++++++ .../solmate/src/utils/FixedPointMathLib.sol | 222 +++++++++++++++ .../lib/solmate/src/utils/ReentrancyGuard.sol | 19 ++ contracts/lib/solmate/src/utils/SSTORE2.sol | 99 +++++++ .../lib/solmate/src/utils/SafeCastLib.sol | 61 +++++ .../lib/solmate/src/utils/SafeTransferLib.sol | 126 +++++++++ contracts/test/_fixture.js | 2 + contracts/test/{token.js => token/ousd.js} | 4 +- contracts/test/token/wousd.js | 78 ++++++ 32 files changed, 2437 insertions(+), 2 deletions(-) create mode 100644 brownie/scripts/wrapped_ousd_test.py create mode 100644 contracts/contracts/token/WrappedOusd.sol create mode 100644 contracts/deploy/039_wrapped_ousd.js create mode 100644 contracts/lib/solmate/.dapprc create mode 100644 contracts/lib/solmate/.gas-snapshot create mode 100644 contracts/lib/solmate/.gitattributes create mode 100644 contracts/lib/solmate/.github/pull_request_template.md create mode 100644 contracts/lib/solmate/.github/workflows/dapp-tests.yml create mode 100644 contracts/lib/solmate/.github/workflows/forge-tests.yml create mode 100644 contracts/lib/solmate/.gitignore create mode 100644 contracts/lib/solmate/.gitmodules create mode 100644 contracts/lib/solmate/.prettierrc create mode 100644 contracts/lib/solmate/.vscode/settings.json create mode 100644 contracts/lib/solmate/src/auth/Auth.sol create mode 100644 contracts/lib/solmate/src/auth/authorities/MultiRolesAuthority.sol create mode 100644 contracts/lib/solmate/src/auth/authorities/RolesAuthority.sol create mode 100644 contracts/lib/solmate/src/mixins/ERC4626.sol create mode 100644 contracts/lib/solmate/src/tokens/ERC1155.sol create mode 100644 contracts/lib/solmate/src/tokens/ERC20.sol create mode 100644 contracts/lib/solmate/src/tokens/ERC721.sol create mode 100644 contracts/lib/solmate/src/tokens/WETH.sol create mode 100644 contracts/lib/solmate/src/utils/Bytes32AddressLib.sol create mode 100644 contracts/lib/solmate/src/utils/CREATE3.sol create mode 100644 contracts/lib/solmate/src/utils/FixedPointMathLib.sol create mode 100644 contracts/lib/solmate/src/utils/ReentrancyGuard.sol create mode 100644 contracts/lib/solmate/src/utils/SSTORE2.sol create mode 100644 contracts/lib/solmate/src/utils/SafeCastLib.sol create mode 100644 contracts/lib/solmate/src/utils/SafeTransferLib.sol rename contracts/test/{token.js => token/ousd.js} (99%) create mode 100644 contracts/test/token/wousd.js diff --git a/brownie/scripts/wrapped_ousd_test.py b/brownie/scripts/wrapped_ousd_test.py new file mode 100644 index 0000000000..3230bf7ccd --- /dev/null +++ b/brownie/scripts/wrapped_ousd_test.py @@ -0,0 +1,111 @@ +from world import * +import random + +# Designed to be used on a fork test +wrapper = load_contract('wrapped_ousd', '0x20E0c5F61124D184101a0A8d9afaeA69F5dAB907') + + +NUM_TESTS = 100 +MAX_REBASE_INCREASE = 0.1 + +ALICE = "0x0a4c79ce84202b03e95b7a692e5d728d83c44c76" +ALLEN = "0x2b6ed29a95753c3ad948348e3e7b1a251080ffb9" +TWINS_A = [ALICE, ALLEN] + +SARAH = "0x002e08000acbbae2155fab7ac01929564949070d" +SHAWN = "0x9845e1909dca337944a0272f1f9f7249833d2d19" +TWINS_B = [SARAH, SHAWN] + +TWINS_C = [SARAH.replace('d','a'), SHAWN.replace('d','a')] + +ALL_TWINS = [TWINS_A, TWINS_B, TWINS_C] + +# Unlocks +unlock(BINANCE) +funder = load_contract("forcefund", "0x21a755560e746289b60b9db3237a556097de01f8") + +unlock(vault_core.address) +funder.fund(vault_core, {'from': BINANCE, 'value': 1e18 }) + +for account in [ALICE, ALLEN, SARAH, SHAWN, TWINS_C[0], TWINS_C[1]]: + unlock(account) + funder.fund(account, {'from': BINANCE, 'value': 1e18 }) + ousd.approve(wrapper, 1e70, {'from': account}) + + +def deposit_some(twins): + wrap_twin, control_twin = twins + amount = int(random.randint(0, 1e18)*random.randint(0, 1e9) + random.randint(0, 1e18)) + print("Depositing: %s {" % amount) + ousd.mint(control_twin, amount, {'from': vault_core, 'gas_limit': 10000000, 'allow_revert': True}) + ousd.mint(wrap_twin, amount, {'from': vault_core, 'gas_limit': 10000000, 'allow_revert': True}) + print(ousd.balanceOf(wrap_twin)) + deposit_amount = min(amount, ousd.balanceOf(wrap_twin)) + wrapper.deposit(deposit_amount, wrap_twin, {'from': wrap_twin, 'gas_limit': 10000000, 'allow_revert': True}) + print("}") + + + +def withdraw_some(twins): + wrap_twin, control_twin = twins + max_withdraw = wrapper.convertToAssets(wrapper.balanceOf(wrap_twin)) + amount = int(max_withdraw * random.random()) + amount = int(max(5, amount - random.randint(0, 1e18))) + print("Withdrawing: %s {" % amount) + wrapper.withdraw(amount, wrap_twin, wrap_twin, {'from': wrap_twin, 'gas_limit': 10000000, 'allow_revert': True}) + print("}") + + +def withdraw_all(twins): + wrap_twin, control_twin = twins + wrapper.redeem(wrapper.balanceOf(wrap_twin), wrap_twin, wrap_twin,{'from': wrap_twin}) + + +def increase_supply(): + new_balance = int(ousd.totalSupply() * (random.random() * MAX_REBASE_INCREASE + 1.0)) + new_balance += random.randint(0, 1e18) + print("Rebasing up %s OUSD" % c18(new_balance)) + ousd.changeSupply(int(new_balance), {'from': vault_core}) + +def print_balance_diffs(all_twins): + diffs = [] + for twins in all_twins: + control_balance = ousd.balanceOf(twins[1]) + wrapper_balance = wrapper.maxWithdraw(twins[0])+ousd.balanceOf(twins[0]) + diffs.append(str(wrapper_balance - control_balance)) + print("🧑‍🚒"," ".join(diffs)) + + +# Run test + +chain.snapshot() + +deposit_some(TWINS_C) +deposit_some(TWINS_B) +deposit_some(TWINS_A) + +for i in range(0, NUM_TESTS): + print("------ %d ------" % i) + twins = [TWINS_A, TWINS_B][random.randint(0,1)] + if random.randint(0, 1) == 1: + deposit_some(twins) + else: + withdraw_some(twins) + print_balance_diffs(ALL_TWINS) + print_balance_diffs(ALL_TWINS) + increase_supply() + print_balance_diffs(ALL_TWINS) + print_balance_diffs(ALL_TWINS) + +# After test + +withdraw_all(TWINS_A) +withdraw_all(TWINS_B) +withdraw_all(TWINS_C) + +print(ousd.balanceOf(TWINS_A[0])) +print(ousd.balanceOf(TWINS_A[1])) +print(ousd.balanceOf(TWINS_B[0])) +print(ousd.balanceOf(TWINS_B[1])) +print(ousd.balanceOf(TWINS_C[0])) +print(ousd.balanceOf(TWINS_C[1])) diff --git a/contracts/contracts/token/WrappedOusd.sol b/contracts/contracts/token/WrappedOusd.sol new file mode 100644 index 0000000000..c6152c40d5 --- /dev/null +++ b/contracts/contracts/token/WrappedOusd.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { ERC20 } from "../../lib/solmate/src/tokens/ERC20.sol"; +import { ERC4626 } from "../../lib/solmate/src/mixins/ERC4626.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import { OUSD } from "./OUSD.sol"; +import { Governable } from "../governance/Governable.sol"; + +contract WrappedOusd is ERC4626, Governable { + using SafeERC20 for IERC20; + + constructor( + ERC20 _underlying, + string memory _name, + string memory _symbol + ) ERC4626(_underlying, _name, _symbol) Governable() { + OUSD(address(_underlying)).rebaseOptOut(); // It's not treated as a contract yet + OUSD(address(_underlying)).rebaseOptIn(); + } + + /** + * @notice Show the total amount of OUSD held by the wrapper + */ + function totalAssets() public view override returns (uint256) { + return ERC20(asset).balanceOf(address(this)); + } + + /** + * @notice Transfer token to governor. Intended for recovering tokens stuck in + * contract, i.e. mistaken sends. Cannot transfer OUSD + * @param _asset Address for the asset + * @param _amount Amount of the asset to transfer + */ + function transferToken(address _asset, uint256 _amount) + external + onlyGovernor + { + require(_asset != address(asset), "Cannot collect OUSD"); + IERC20(_asset).safeTransfer(governor(), _amount); + } +} diff --git a/contracts/deploy/001_core.js b/contracts/deploy/001_core.js index 564d40ab90..48afb4eeb6 100644 --- a/contracts/deploy/001_core.js +++ b/contracts/deploy/001_core.js @@ -706,6 +706,21 @@ const deployVaultVaultChecker = async () => { await deployWithConfirmation("VaultValueChecker", [vault.address]); }; +const deployWOusd = async () => { + const { deployerAddr, governorAddr } = await getNamedAccounts(); + const sDeployer = await ethers.provider.getSigner(deployerAddr); + const sGovernor = await ethers.provider.getSigner(governorAddr); + const ousd = await ethers.getContract("OUSDProxy"); + await deployWithConfirmation("WrappedOusd", [ + ousd.address, + "Wrapped OUSD", + "WOUSD", + ]); + const wousd = await ethers.getContract("WrappedOusd"); + await wousd.connect(sDeployer).transferGovernance(governorAddr); + await wousd.connect(sGovernor).claimGovernance(); +}; + const main = async () => { console.log("Running 001_core deployment..."); await deployOracles(); @@ -722,6 +737,7 @@ const main = async () => { await deployBuyback(); await deployUniswapV3Pool(); await deployVaultVaultChecker(); + await deployWOusd(); console.log("001_core deploy done."); return true; }; diff --git a/contracts/deploy/039_wrapped_ousd.js b/contracts/deploy/039_wrapped_ousd.js new file mode 100644 index 0000000000..42ab653bf5 --- /dev/null +++ b/contracts/deploy/039_wrapped_ousd.js @@ -0,0 +1,47 @@ +const { deploymentWithProposal, withConfirmation } = require("../utils/deploy"); + +// Deploy new staking implimentation contract with fix +// Upgrade to using it + +module.exports = deploymentWithProposal( + { deployName: "039_wrapped_ousd", forceDeploy: true }, + async ({ deployWithConfirmation, getTxOpts, ethers }) => { + const { deployerAddr, governorAddr } = await getNamedAccounts(); + const sDeployer = await ethers.provider.getSigner(deployerAddr); + + // Current contracts + const cOUSDProxy = await ethers.getContract("OUSDProxy"); + + // Deployer Actions + // ---------------- + + // 1. Deploy the new implementation. + const dWrappedOusd = await deployWithConfirmation("WrappedOusd", [ + cOUSDProxy.address, + "Wrapped OUSD", + "WOUSD", + ]); + const cWousd = await ethers.getContract("WrappedOusd"); + + // 2. Assign ownership + await withConfirmation( + cWousd + .connect(sDeployer) + .transferGovernance(governorAddr, await getTxOpts()) + ); + + // Governance Actions + // ---------------- + + return { + name: "Claim WOUSD Governance", + actions: [ + // 1. Claim governance + { + contract: cWousd, + signature: "claimGovernance()", + }, + ] + } + } +); diff --git a/contracts/lib/solmate/.dapprc b/contracts/lib/solmate/.dapprc new file mode 100644 index 0000000000..e5524b4b72 --- /dev/null +++ b/contracts/lib/solmate/.dapprc @@ -0,0 +1,14 @@ +# Basic build/test configuration. +export DAPP_SOLC_VERSION=0.8.10 +export DAPP_BUILD_OPTIMIZE=1 +export DAPP_BUILD_OPTIMIZE_RUNS=1000000 +export DAPP_LINK_TEST_LIBRARIES=0 +export DAPP_TEST_VERBOSITY=1 +export DAPP_TEST_SMTTIMEOUT=500000 + +if [ "$DEEP_FUZZ" = "true" ] +then + export DAPP_TEST_FUZZ_RUNS=10000 # Fuzz for a long time if DEEP_FUZZ is set to true. +else + export DAPP_TEST_FUZZ_RUNS=100 # Only fuzz briefly if DEEP_FUZZ is not set to true. +fi diff --git a/contracts/lib/solmate/.gas-snapshot b/contracts/lib/solmate/.gas-snapshot new file mode 100644 index 0000000000..73bea51220 --- /dev/null +++ b/contracts/lib/solmate/.gas-snapshot @@ -0,0 +1,215 @@ +testFailSetAuthorityWithRestrictiveAuthority() (gas: 126002) +testSetAuthorityWithPermissiveAuthority() (gas: 127687) +testFailSetOwnerWithRestrictiveAuthority() (gas: 126166) +testFailCallFunctionAsNonOwner() (gas: 4191) +testSetAuthorityAsOwner() (gas: 23802) +testFailCallFunctionAsOwnerWithOutOfOrderAuthority() (gas: 135733) +testCallFunctionWithPermissiveAuthority() (gas: 125973) +testFailSetAuthorityAsNonOwner() (gas: 6960) +testFailSetOwnerAsOwnerWithOutOfOrderAuthority() (gas: 135873) +testCallFunctionAsOwner() (gas: 21371) +testFailCallFunctionWithRestrictiveAuthority() (gas: 126125) +testSetOwnerWithPermissiveAuthority() (gas: 147508) +testFailSetOwnerAsNonOwner() (gas: 4309) +testSetAuthorityAsOwnerWithOutOfOrderAuthority() (gas: 234329) +testSetOwnerAsOwner() (gas: 3998) +testFromLast20Bytes() (gas: 191) +testFillLast12Bytes() (gas: 223) +testFailDoubleDeploySameBytecode() (gas: 277076930206699) +testDeployERC20() (gas: 860923) +testFailDoubleDeployDifferentBytecode() (gas: 277076930214644) +testFailBoundMinBiggerThanMax() (gas: 309) +testBound() (gas: 16755) +testFailSafeBatchTransferFromToRevertingERC1155Recipient() (gas: 1041059) +testMintToEOA() (gas: 30265) +testFailMintToNonERC155Recipient() (gas: 71897) +testFailSafeBatchTransferFromToZero() (gas: 805760) +testBatchMintToERC1155Recipient() (gas: 946375) +testApproveAll() (gas: 26509) +testFailSafeBatchTransferFromWithArrayLengthMismatch() (gas: 681042) +testFailBatchMintToZero() (gas: 127242) +testFailSafeBatchTransferFromToWrongReturnDataERC1155Recipient() (gas: 992983) +testSafeTransferFromToERC1155Recipient() (gas: 1210543) +testFailBatchMintToWrongReturnDataERC1155Recipient() (gas: 314473) +testFailBatchMintToRevertingERC1155Recipient() (gas: 362536) +testBatchBurn() (gas: 146591) +testFailBurnInsufficientBalance() (gas: 30352) +testFailSafeTransferFromToWrongReturnDataERC1155Recipient() (gas: 243471) +testFailMintToRevertingERC155Recipient() (gas: 263148) +testFailSafeBatchTransferFromToNonERC1155Recipient() (gas: 849517) +testFailSafeTransferFromInsufficientBalance() (gas: 579173) +testFailSafeTransferFromToNonERC155Recipient() (gas: 100376) +testFailBatchMintToNonERC1155Recipient() (gas: 171010) +testSafeBatchTransferFromToEOA() (gas: 817022) +testFailSafeTransferFromToRevertingERC1155Recipient() (gas: 291604) +testBatchMintToEOA() (gas: 132842) +testFailBatchBurnInsufficientBalance() (gas: 131673) +testSafeBatchTransferFromToERC1155Recipient() (gas: 1650404) +testFailBalanceOfBatchWithArrayMismatch() (gas: 4794) +testFailSafeBatchTransferInsufficientBalance() (gas: 682003) +testSafeTransferFromToEOA() (gas: 609087) +testMintToERC1155Recipient() (gas: 612041) +testFailBatchMintWithArrayMismatch() (gas: 5118) +testBatchBalanceOf() (gas: 153791) +testFailSafeTransferFromToZero() (gas: 57667) +testFailSafeTransferFromSelfInsufficientBalance() (gas: 29956) +testBurn() (gas: 34098) +testFailBatchBurnWithArrayLengthMismatch() (gas: 131065) +testFailMintToZero() (gas: 29205) +testSafeTransferFromSelf() (gas: 59828) +testFailMintToWrongReturnDataERC155Recipient() (gas: 263102) +testInfiniteApproveTransferFrom() (gas: 387818) +testApprove() (gas: 26558) +testTransferFrom() (gas: 388112) +testFailTransferFromInsufficientBalance() (gas: 359489) +testFailPermitPastDeadline() (gas: 1489) +testFailPermitReplay() (gas: 59085) +testMint() (gas: 49246) +testFailTransferFromInsufficientAllowance() (gas: 359013) +testTransfer() (gas: 75672) +testBurn() (gas: 52470) +testPermit() (gas: 55993) +testFailTransferInsufficientBalance() (gas: 48306) +testFailPermitBadDeadline() (gas: 29724) +testFailPermitBadNonce() (gas: 29674) +testFailRedeemWithNoShareAmount() (gas: 25839) +testFailRedeemWithNotEnoughShareAmount() (gas: 190632) +testFailWithdrawWithNoUnderlyingAmount() (gas: 25792) +testFailMintWithNoApproval() (gas: 6296) +testFailDepositWithNotEnoughApproval() (gas: 77987) +testFailRedeemZero() (gas: 3461) +testFailWithdrawWithNotEnoughUnderlyingAmount() (gas: 190615) +testFailDepositZero() (gas: 3274) +testMultipleMintDepositRedeemWithdraw() (gas: 1446573) +testWithdrawZero() (gas: 43465) +testFailDepositWithNoApproval() (gas: 6351) +testVaultInteractionsForSomeoneElse() (gas: 1287566) +testMintZero() (gas: 45598) +testSafeTransferFromToERC721Recipient() (gas: 885703) +testFailSafeMintToERC721RecipientWithWrongReturnDataWithData() (gas: 163605) +testApprove() (gas: 73904) +testFailBurnUnMinted() (gas: 3379) +testFailSafeTransferFromToERC721RecipientWithWrongReturnDataWithData() (gas: 191740) +testFailDoubleMint() (gas: 48808) +testApproveAll() (gas: 26585) +testFailApproveUnAuthorized() (gas: 51054) +testFailSafeTransferFromToRevertingERC721RecipientWithData() (gas: 237450) +testFailSafeMintToNonERC721RecipientWithData() (gas: 93740) +testFailTransferFromWrongFrom() (gas: 48838) +testFailSafeMintToRevertingERC721Recipient() (gas: 208477) +testTransferFrom() (gas: 530630) +testFailSafeMintToNonERC721Recipient() (gas: 92893) +testFailDoubleBurn() (gas: 54465) +testFailSafeMintToERC721RecipientWithWrongReturnData() (gas: 162744) +testFailSafeTransferFromToNonERC721Recipient() (gas: 121141) +testMint() (gas: 49778) +testFailApproveUnMinted() (gas: 5672) +testFailTransferFromToZero() (gas: 48903) +testSafeMintToERC721Recipient() (gas: 385391) +testSafeTransferFromToEOA() (gas: 533049) +testSafeMintToEOA() (gas: 52413) +testApproveBurn() (gas: 79785) +testFailSafeTransferFromToERC721RecipientWithWrongReturnData() (gas: 190988) +testTransferFromApproveAll() (gas: 530281) +testFailTransferFromUnOwned() (gas: 3500) +testFailSafeTransferFromToNonERC721RecipientWithData() (gas: 121921) +testBurn() (gas: 55481) +testFailSafeMintToRevertingERC721RecipientWithData() (gas: 209269) +testFailMintToZero() (gas: 1253) +testFailTransferFromNotOwner() (gas: 53372) +testSafeMintToERC721RecipientWithData() (gas: 406553) +testFailSafeTransferFromToRevertingERC721Recipient() (gas: 236721) +testSafeTransferFromToERC721RecipientWithData() (gas: 906865) +testTransferFromSelf() (gas: 80050) +testMulWadDown() (gas: 821) +testDivWadDownEdgeCases() (gas: 423) +testFailDivWadUpZeroDenominator() (gas: 342) +testDivWadUp() (gas: 981) +testMulWadDownEdgeCases() (gas: 886) +testFailMulDivUpZeroDenominator() (gas: 317) +testMulDivUpEdgeCases() (gas: 846) +testDivWadUpEdgeCases() (gas: 482) +testFailDivWadDownZeroDenominator() (gas: 362) +testRPow() (gas: 2142) +testMulDivDownEdgeCases() (gas: 751) +testSqrt() (gas: 2537) +testDivWadDown() (gas: 864) +testMulDivDown() (gas: 1861) +testMulWadUpEdgeCases() (gas: 1002) +testMulWadUp() (gas: 959) +testFailMulDivDownZeroDenominator() (gas: 316) +testMulDivUp() (gas: 2273) +testSetRoles() (gas: 33023) +testCanCallWithCustomAuthorityOverridesPublicCapability() (gas: 295417) +testCanCallPublicCapability() (gas: 39631) +testSetTargetCustomAuthority() (gas: 31736) +testCanCallWithCustomAuthorityOverridesUserWithRole() (gas: 334265) +testCanCallWithAuthorizedRole() (gas: 97461) +testSetRoleCapabilities() (gas: 32997) +testCanCallWithCustomAuthority() (gas: 466959) +testSetPublicCapabilities() (gas: 31468) +testNoReentrancy() (gas: 1015) +testProtectedCall() (gas: 23649) +testFailUnprotectedCall() (gas: 30515) +testSetRoles() (gas: 32998) +testCanCallPublicCapability() (gas: 38436) +testCanCallWithAuthorizedRole() (gas: 96267) +testSetRoleCapabilities() (gas: 34588) +testSetPublicCapabilities() (gas: 33244) +testWriteRead() (gas: 53511) +testWriteReadFullStartBound() (gas: 34725) +testFailWriteReadEmptyOutOfBounds() (gas: 34432) +testWriteReadFullBoundedRead() (gas: 53708) +testFailReadInvalidPointer() (gas: 2905) +testFailWriteReadOutOfStartBound() (gas: 34346) +testFailReadInvalidPointerCustomStartBound() (gas: 2982) +testWriteReadEmptyBound() (gas: 34639) +testFailWriteReadOutOfBounds() (gas: 34453) +testWriteReadCustomBounds() (gas: 34853) +testWriteReadCustomStartBound() (gas: 34768) +testFailReadInvalidPointerCustomBounds() (gas: 3143) +testSafeCastTo248() (gas: 427) +testSafeCastTo128() (gas: 449) +testSafeCastTo32() (gas: 471) +testFailSafeCastTo192() (gas: 344) +testSafeCastTo192() (gas: 471) +testFailSafeCastTo96() (gas: 321) +testSafeCastTo96() (gas: 469) +testSafeCastTo224() (gas: 491) +testFailSafeCastTo8() (gas: 296) +testFailSafeCastTo64() (gas: 321) +testSafeCastTo64() (gas: 470) +testFailSafeCastTo248() (gas: 365) +testFailSafeCastTo224() (gas: 343) +testFailSafeCastTo128() (gas: 321) +testSafeCastTo160() (gas: 470) +testFailSafeCastTo160() (gas: 342) +testFailSafeCastTo32() (gas: 364) +testSafeCastTo8() (gas: 469) +testFailTransferWithReturnsFalse() (gas: 4032) +testFailTransferFromWithReverting() (gas: 5045) +testApproveWithStandardERC20() (gas: 26339) +testTransferFromWithReturnsTooMuch() (gas: 58018) +testFailTransferFromWithReturnsFalse() (gas: 8950) +testApproveWithNonContract() (gas: 3014) +testApproveWithMissingReturn() (gas: 26274) +testFailTransferWithReturnsTooLittle() (gas: 3973) +testApproveWithReturnsTooMuch() (gas: 26655) +testTransferFromWithMissingReturn() (gas: 57195) +testTransferWithStandardERC20() (gas: 27422) +testFailTransferFromWithReturnsTooLittle() (gas: 8788) +testTransferFromWithStandardERC20() (gas: 57282) +testTransferFromWithNonContract() (gas: 3036) +testFailApproveWithReturnsTooLittle() (gas: 1069) +testTransferWithMissingReturn() (gas: 27368) +testFailApproveWithReturnsFalse() (gas: 1126) +testTransferETH() (gas: 34637) +testTransferWithNonContract() (gas: 2990) +testTransferWithReturnsTooMuch() (gas: 27793) +testFailTransferETHToContractWithoutFallback() (gas: 7244) +testFailApproveWithReverting() (gas: 1024) +testFailTransferWithReverting() (gas: 4016) +testPartialWithdraw() (gas: 68781) +testDeposit() (gas: 58760) +testFallbackDeposit() (gas: 59024) +testWithdraw() (gas: 68715) diff --git a/contracts/lib/solmate/.gitattributes b/contracts/lib/solmate/.gitattributes new file mode 100644 index 0000000000..745230d431 --- /dev/null +++ b/contracts/lib/solmate/.gitattributes @@ -0,0 +1,3 @@ +*.sol linguist-language=Solidity +.dapprc linguist-language=Shell +.gas-snapshot linguist-language=Julia \ No newline at end of file diff --git a/contracts/lib/solmate/.github/pull_request_template.md b/contracts/lib/solmate/.github/pull_request_template.md new file mode 100644 index 0000000000..4f03af8b6f --- /dev/null +++ b/contracts/lib/solmate/.github/pull_request_template.md @@ -0,0 +1,14 @@ +## Description + +Describe the changes made in your pull request here. + +## Checklist + +Ensure you completed **all of the steps** below before submitting your pull request: + +- [ ] Ran `dapp snapshot`? +- [ ] Ran `npm run lint`? +- [ ] Ran `forge test`? +- [ ] Ran `dapp test`? + +_Pull requests with an incomplete checklist will be thrown out._ diff --git a/contracts/lib/solmate/.github/workflows/dapp-tests.yml b/contracts/lib/solmate/.github/workflows/dapp-tests.yml new file mode 100644 index 0000000000..e3a8df5c32 --- /dev/null +++ b/contracts/lib/solmate/.github/workflows/dapp-tests.yml @@ -0,0 +1,27 @@ +name: Dapp Tests + +on: [push, pull_request] + +jobs: + dapp-tests: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + - uses: cachix/install-nix-action@v13 + - uses: cachix/cachix-action@v10 + with: + name: dapp + + - name: Install dependencies + run: nix-shell --run 'make' + + - name: Check gas snapshots + run: nix-shell --run 'dapp check-snapshot' + + - name: Run tests + run: nix-shell --run 'dapp test' + env: + # Only fuzz deeply if we're pushing to main or this is a PR to main: + DEEP_FUZZ: ${{ github.ref == 'refs/heads/main' || github.base_ref == 'main' }} diff --git a/contracts/lib/solmate/.github/workflows/forge-tests.yml b/contracts/lib/solmate/.github/workflows/forge-tests.yml new file mode 100644 index 0000000000..6c5ef5a23d --- /dev/null +++ b/contracts/lib/solmate/.github/workflows/forge-tests.yml @@ -0,0 +1,20 @@ +name: Forge Tests + +on: [push, pull_request] + +jobs: + forge-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Install Foundry + uses: onbjerg/foundry-toolchain@v1 + with: + version: nightly + + - name: Install dependencies + run: forge install + + - name: Run forge tests + run: forge test diff --git a/contracts/lib/solmate/.gitignore b/contracts/lib/solmate/.gitignore new file mode 100644 index 0000000000..5dfe93fbde --- /dev/null +++ b/contracts/lib/solmate/.gitignore @@ -0,0 +1,3 @@ +/cache +/node_modules +/out \ No newline at end of file diff --git a/contracts/lib/solmate/.gitmodules b/contracts/lib/solmate/.gitmodules new file mode 100644 index 0000000000..e12471968b --- /dev/null +++ b/contracts/lib/solmate/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/ds-test"] + path = lib/ds-test + url = https://github.com/dapphub/ds-test diff --git a/contracts/lib/solmate/.prettierrc b/contracts/lib/solmate/.prettierrc new file mode 100644 index 0000000000..15ae8a76d4 --- /dev/null +++ b/contracts/lib/solmate/.prettierrc @@ -0,0 +1,14 @@ +{ + "tabWidth": 2, + "printWidth": 100, + + "overrides": [ + { + "files": "*.sol", + "options": { + "tabWidth": 4, + "printWidth": 120 + } + } + ] +} diff --git a/contracts/lib/solmate/.vscode/settings.json b/contracts/lib/solmate/.vscode/settings.json new file mode 100644 index 0000000000..c06f7e059a --- /dev/null +++ b/contracts/lib/solmate/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "solidity.packageDefaultDependenciesContractsDirectory": "src", + "solidity.packageDefaultDependenciesDirectory": "lib", + "solidity.compileUsingRemoteVersion": "v0.8.10", + "search.exclude": { "lib": true }, + "files.associations": { + ".dapprc": "shellscript", + ".gas-snapshot": "julia" + } +} diff --git a/contracts/lib/solmate/src/auth/Auth.sol b/contracts/lib/solmate/src/auth/Auth.sol new file mode 100644 index 0000000000..2cf7559217 --- /dev/null +++ b/contracts/lib/solmate/src/auth/Auth.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.0; + +/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Auth.sol) +/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol) +abstract contract Auth { + event OwnerUpdated(address indexed user, address indexed newOwner); + + event AuthorityUpdated(address indexed user, Authority indexed newAuthority); + + address public owner; + + Authority public authority; + + constructor(address _owner, Authority _authority) { + owner = _owner; + authority = _authority; + + emit OwnerUpdated(msg.sender, _owner); + emit AuthorityUpdated(msg.sender, _authority); + } + + modifier requiresAuth() { + require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED"); + + _; + } + + function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) { + Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas. + + // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be + // aware that this makes protected functions uncallable even to the owner if the authority is out of order. + return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner; + } + + function setAuthority(Authority newAuthority) public virtual { + // We check if the caller is the owner first because we want to ensure they can + // always swap out the authority even if it's reverting or using up a lot of gas. + require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig)); + + authority = newAuthority; + + emit AuthorityUpdated(msg.sender, newAuthority); + } + + function setOwner(address newOwner) public virtual requiresAuth { + owner = newOwner; + + emit OwnerUpdated(msg.sender, newOwner); + } +} + +/// @notice A generic interface for a contract which provides authorization data to an Auth instance. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Auth.sol) +/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol) +interface Authority { + function canCall( + address user, + address target, + bytes4 functionSig + ) external view returns (bool); +} diff --git a/contracts/lib/solmate/src/auth/authorities/MultiRolesAuthority.sol b/contracts/lib/solmate/src/auth/authorities/MultiRolesAuthority.sol new file mode 100644 index 0000000000..3329714c9a --- /dev/null +++ b/contracts/lib/solmate/src/auth/authorities/MultiRolesAuthority.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity 0.8.10; + +import {Auth, Authority} from "../Auth.sol"; + +/// @notice Flexible and target agnostic role based Authority that supports up to 256 roles. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/authorities/MultiRolesAuthority.sol) +contract MultiRolesAuthority is Auth, Authority { + /*/////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event UserRoleUpdated(address indexed user, uint8 indexed role, bool enabled); + + event PublicCapabilityUpdated(bytes4 indexed functionSig, bool enabled); + + event RoleCapabilityUpdated(uint8 indexed role, bytes4 indexed functionSig, bool enabled); + + event TargetCustomAuthorityUpdated(address indexed target, Authority indexed authority); + + /*/////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor(address _owner, Authority _authority) Auth(_owner, _authority) {} + + /*/////////////////////////////////////////////////////////////// + CUSTOM TARGET AUTHORITY STORAGE + //////////////////////////////////////////////////////////////*/ + + mapping(address => Authority) public getTargetCustomAuthority; + + /*/////////////////////////////////////////////////////////////// + ROLE/USER STORAGE + //////////////////////////////////////////////////////////////*/ + + mapping(address => bytes32) public getUserRoles; + + mapping(bytes4 => bool) public isCapabilityPublic; + + mapping(bytes4 => bytes32) public getRolesWithCapability; + + function doesUserHaveRole(address user, uint8 role) public view virtual returns (bool) { + return (uint256(getUserRoles[user]) >> role) & 1 != 0; + } + + function doesRoleHaveCapability(uint8 role, bytes4 functionSig) public view virtual returns (bool) { + return (uint256(getRolesWithCapability[functionSig]) >> role) & 1 != 0; + } + + /*/////////////////////////////////////////////////////////////// + AUTHORIZATION LOGIC + //////////////////////////////////////////////////////////////*/ + + function canCall( + address user, + address target, + bytes4 functionSig + ) public view virtual override returns (bool) { + Authority customAuthority = getTargetCustomAuthority[target]; + + if (address(customAuthority) != address(0)) return customAuthority.canCall(user, target, functionSig); + + return + isCapabilityPublic[functionSig] || bytes32(0) != getUserRoles[user] & getRolesWithCapability[functionSig]; + } + + /*/////////////////////////////////////////////////////////////// + CUSTOM TARGET AUTHORITY CONFIGURATION LOGIC + //////////////////////////////////////////////////////////////*/ + + function setTargetCustomAuthority(address target, Authority customAuthority) public virtual requiresAuth { + getTargetCustomAuthority[target] = customAuthority; + + emit TargetCustomAuthorityUpdated(target, customAuthority); + } + + /*/////////////////////////////////////////////////////////////// + PUBLIC CAPABILITY CONFIGURATION LOGIC + //////////////////////////////////////////////////////////////*/ + + function setPublicCapability(bytes4 functionSig, bool enabled) public virtual requiresAuth { + isCapabilityPublic[functionSig] = enabled; + + emit PublicCapabilityUpdated(functionSig, enabled); + } + + /*/////////////////////////////////////////////////////////////// + USER ROLE ASSIGNMENT LOGIC + //////////////////////////////////////////////////////////////*/ + + function setUserRole( + address user, + uint8 role, + bool enabled + ) public virtual requiresAuth { + if (enabled) { + getUserRoles[user] |= bytes32(1 << role); + } else { + getUserRoles[user] &= ~bytes32(1 << role); + } + + emit UserRoleUpdated(user, role, enabled); + } + + /*/////////////////////////////////////////////////////////////// + ROLE CAPABILITY CONFIGURATION LOGIC + //////////////////////////////////////////////////////////////*/ + + function setRoleCapability( + uint8 role, + bytes4 functionSig, + bool enabled + ) public virtual requiresAuth { + if (enabled) { + getRolesWithCapability[functionSig] |= bytes32(1 << role); + } else { + getRolesWithCapability[functionSig] &= ~bytes32(1 << role); + } + + emit RoleCapabilityUpdated(role, functionSig, enabled); + } +} diff --git a/contracts/lib/solmate/src/auth/authorities/RolesAuthority.sol b/contracts/lib/solmate/src/auth/authorities/RolesAuthority.sol new file mode 100644 index 0000000000..94e394f6a4 --- /dev/null +++ b/contracts/lib/solmate/src/auth/authorities/RolesAuthority.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.0; + +import {Auth, Authority} from "../Auth.sol"; + +/// @notice Role based Authority that supports up to 256 roles. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/authorities/RolesAuthority.sol) +/// @author Modified from Dappsys (https://github.com/dapphub/ds-roles/blob/master/src/roles.sol) +contract RolesAuthority is Auth, Authority { + /*/////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event UserRoleUpdated(address indexed user, uint8 indexed role, bool enabled); + + event PublicCapabilityUpdated(address indexed target, bytes4 indexed functionSig, bool enabled); + + event RoleCapabilityUpdated(uint8 indexed role, address indexed target, bytes4 indexed functionSig, bool enabled); + + /*/////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor(address _owner, Authority _authority) Auth(_owner, _authority) {} + + /*/////////////////////////////////////////////////////////////// + ROLE/USER STORAGE + //////////////////////////////////////////////////////////////*/ + + mapping(address => bytes32) public getUserRoles; + + mapping(address => mapping(bytes4 => bool)) public isCapabilityPublic; + + mapping(address => mapping(bytes4 => bytes32)) public getRolesWithCapability; + + function doesUserHaveRole(address user, uint8 role) public view virtual returns (bool) { + return (uint256(getUserRoles[user]) >> role) & 1 != 0; + } + + function doesRoleHaveCapability( + uint8 role, + address target, + bytes4 functionSig + ) public view virtual returns (bool) { + return (uint256(getRolesWithCapability[target][functionSig]) >> role) & 1 != 0; + } + + /*/////////////////////////////////////////////////////////////// + AUTHORIZATION LOGIC + //////////////////////////////////////////////////////////////*/ + + function canCall( + address user, + address target, + bytes4 functionSig + ) public view virtual override returns (bool) { + return + isCapabilityPublic[target][functionSig] || + bytes32(0) != getUserRoles[user] & getRolesWithCapability[target][functionSig]; + } + + /*/////////////////////////////////////////////////////////////// + ROLE CAPABILITY CONFIGURATION LOGIC + //////////////////////////////////////////////////////////////*/ + + function setPublicCapability( + address target, + bytes4 functionSig, + bool enabled + ) public virtual requiresAuth { + isCapabilityPublic[target][functionSig] = enabled; + + emit PublicCapabilityUpdated(target, functionSig, enabled); + } + + function setRoleCapability( + uint8 role, + address target, + bytes4 functionSig, + bool enabled + ) public virtual requiresAuth { + if (enabled) { + getRolesWithCapability[target][functionSig] |= bytes32(1 << role); + } else { + getRolesWithCapability[target][functionSig] &= ~bytes32(1 << role); + } + + emit RoleCapabilityUpdated(role, target, functionSig, enabled); + } + + /*/////////////////////////////////////////////////////////////// + USER ROLE ASSIGNMENT LOGIC + //////////////////////////////////////////////////////////////*/ + + function setUserRole( + address user, + uint8 role, + bool enabled + ) public virtual requiresAuth { + if (enabled) { + getUserRoles[user] |= bytes32(1 << role); + } else { + getUserRoles[user] &= ~bytes32(1 << role); + } + + emit UserRoleUpdated(user, role, enabled); + } +} diff --git a/contracts/lib/solmate/src/mixins/ERC4626.sol b/contracts/lib/solmate/src/mixins/ERC4626.sol new file mode 100644 index 0000000000..7afc7796c1 --- /dev/null +++ b/contracts/lib/solmate/src/mixins/ERC4626.sol @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.0; + +import {ERC20} from "../tokens/ERC20.sol"; +import {SafeTransferLib} from "../utils/SafeTransferLib.sol"; +import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol"; + +/// @notice Minimal ERC4626 tokenized Vault implementation. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/mixins/ERC4626.sol) +abstract contract ERC4626 is ERC20 { + using SafeTransferLib for ERC20; + using FixedPointMathLib for uint256; + + /*/////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares); + + event Withdraw( + address indexed caller, + address indexed receiver, + address indexed owner, + uint256 assets, + uint256 shares + ); + + /*/////////////////////////////////////////////////////////////// + IMMUTABLES + //////////////////////////////////////////////////////////////*/ + + ERC20 public immutable asset; + + constructor( + ERC20 _asset, + string memory _name, + string memory _symbol + ) ERC20(_name, _symbol, _asset.decimals()) { + asset = _asset; + } + + /*/////////////////////////////////////////////////////////////// + DEPOSIT/WITHDRAWAL LOGIC + //////////////////////////////////////////////////////////////*/ + + function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) { + // Check for rounding error since we round down in previewDeposit. + require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES"); + + // Need to transfer before minting or ERC777s could reenter. + asset.safeTransferFrom(msg.sender, address(this), assets); + + _mint(receiver, shares); + + emit Deposit(msg.sender, receiver, assets, shares); + + afterDeposit(assets, shares); + } + + function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) { + assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up. + + // Need to transfer before minting or ERC777s could reenter. + asset.safeTransferFrom(msg.sender, address(this), assets); + + _mint(receiver, shares); + + emit Deposit(msg.sender, receiver, assets, shares); + + afterDeposit(assets, shares); + } + + function withdraw( + uint256 assets, + address receiver, + address owner + ) public virtual returns (uint256 shares) { + shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up. + + if (msg.sender != owner) { + uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. + + if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; + } + + beforeWithdraw(assets, shares); + + _burn(owner, shares); + + emit Withdraw(msg.sender, receiver, owner, assets, shares); + + asset.safeTransfer(receiver, assets); + } + + function redeem( + uint256 shares, + address receiver, + address owner + ) public virtual returns (uint256 assets) { + if (msg.sender != owner) { + uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. + + if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; + } + + // Check for rounding error since we round down in previewRedeem. + require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS"); + + beforeWithdraw(assets, shares); + + _burn(owner, shares); + + emit Withdraw(msg.sender, receiver, owner, assets, shares); + + asset.safeTransfer(receiver, assets); + } + + /*/////////////////////////////////////////////////////////////// + ACCOUNTING LOGIC + //////////////////////////////////////////////////////////////*/ + + function totalAssets() public view virtual returns (uint256); + + function convertToShares(uint256 assets) public view returns (uint256) { + uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. + + return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets()); + } + + function convertToAssets(uint256 shares) public view returns (uint256) { + uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. + + return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply); + } + + function previewDeposit(uint256 assets) public view virtual returns (uint256) { + return convertToShares(assets); + } + + function previewMint(uint256 shares) public view virtual returns (uint256) { + uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. + + return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply); + } + + function previewWithdraw(uint256 assets) public view virtual returns (uint256) { + uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. + + return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets()); + } + + function previewRedeem(uint256 shares) public view virtual returns (uint256) { + return convertToAssets(shares); + } + + /*/////////////////////////////////////////////////////////////// + DEPOSIT/WITHDRAWAL LIMIT LOGIC + //////////////////////////////////////////////////////////////*/ + + function maxDeposit(address) public view virtual returns (uint256) { + return type(uint256).max; + } + + function maxMint(address) public view virtual returns (uint256) { + return type(uint256).max; + } + + function maxWithdraw(address owner) public view virtual returns (uint256) { + return convertToAssets(balanceOf[owner]); + } + + function maxRedeem(address owner) public view virtual returns (uint256) { + return balanceOf[owner]; + } + + /*/////////////////////////////////////////////////////////////// + INTERNAL HOOKS LOGIC + //////////////////////////////////////////////////////////////*/ + + function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {} + + function afterDeposit(uint256 assets, uint256 shares) internal virtual {} +} diff --git a/contracts/lib/solmate/src/tokens/ERC1155.sol b/contracts/lib/solmate/src/tokens/ERC1155.sol new file mode 100644 index 0000000000..7ec9eb2f14 --- /dev/null +++ b/contracts/lib/solmate/src/tokens/ERC1155.sol @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.0; + +/// @notice Minimalist and gas efficient standard ERC1155 implementation. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol) +abstract contract ERC1155 { + /*/////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event TransferSingle( + address indexed operator, + address indexed from, + address indexed to, + uint256 id, + uint256 amount + ); + + event TransferBatch( + address indexed operator, + address indexed from, + address indexed to, + uint256[] ids, + uint256[] amounts + ); + + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + + event URI(string value, uint256 indexed id); + + /*/////////////////////////////////////////////////////////////// + ERC1155 STORAGE + //////////////////////////////////////////////////////////////*/ + + mapping(address => mapping(uint256 => uint256)) public balanceOf; + + mapping(address => mapping(address => bool)) public isApprovedForAll; + + /*/////////////////////////////////////////////////////////////// + METADATA LOGIC + //////////////////////////////////////////////////////////////*/ + + function uri(uint256 id) public view virtual returns (string memory); + + /*/////////////////////////////////////////////////////////////// + ERC1155 LOGIC + //////////////////////////////////////////////////////////////*/ + + function setApprovalForAll(address operator, bool approved) public virtual { + isApprovedForAll[msg.sender][operator] = approved; + + emit ApprovalForAll(msg.sender, operator, approved); + } + + function safeTransferFrom( + address from, + address to, + uint256 id, + uint256 amount, + bytes memory data + ) public virtual { + require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED"); + + balanceOf[from][id] -= amount; + balanceOf[to][id] += amount; + + emit TransferSingle(msg.sender, from, to, id, amount); + + require( + to.code.length == 0 + ? to != address(0) + : ERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) == + ERC1155TokenReceiver.onERC1155Received.selector, + "UNSAFE_RECIPIENT" + ); + } + + function safeBatchTransferFrom( + address from, + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) public virtual { + uint256 idsLength = ids.length; // Saves MLOADs. + + require(idsLength == amounts.length, "LENGTH_MISMATCH"); + + require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED"); + + // Storing these outside the loop saves ~15 gas per iteration. + uint256 id; + uint256 amount; + + for (uint256 i = 0; i < idsLength; ) { + id = ids[i]; + amount = amounts[i]; + + balanceOf[from][id] -= amount; + balanceOf[to][id] += amount; + + // An array can't have a total length + // larger than the max uint256 value. + unchecked { + ++i; + } + } + + emit TransferBatch(msg.sender, from, to, ids, amounts); + + require( + to.code.length == 0 + ? to != address(0) + : ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) == + ERC1155TokenReceiver.onERC1155BatchReceived.selector, + "UNSAFE_RECIPIENT" + ); + } + + function balanceOfBatch(address[] memory owners, uint256[] memory ids) + public + view + virtual + returns (uint256[] memory balances) + { + uint256 ownersLength = owners.length; // Saves MLOADs. + + require(ownersLength == ids.length, "LENGTH_MISMATCH"); + + balances = new uint256[](ownersLength); + + // Unchecked because the only math done is incrementing + // the array index counter which cannot possibly overflow. + unchecked { + for (uint256 i = 0; i < ownersLength; ++i) { + balances[i] = balanceOf[owners[i]][ids[i]]; + } + } + } + + /*/////////////////////////////////////////////////////////////// + ERC165 LOGIC + //////////////////////////////////////////////////////////////*/ + + function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { + return + interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 + interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155 + interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI + } + + /*/////////////////////////////////////////////////////////////// + INTERNAL MINT/BURN LOGIC + //////////////////////////////////////////////////////////////*/ + + function _mint( + address to, + uint256 id, + uint256 amount, + bytes memory data + ) internal { + balanceOf[to][id] += amount; + + emit TransferSingle(msg.sender, address(0), to, id, amount); + + require( + to.code.length == 0 + ? to != address(0) + : ERC1155TokenReceiver(to).onERC1155Received(msg.sender, address(0), id, amount, data) == + ERC1155TokenReceiver.onERC1155Received.selector, + "UNSAFE_RECIPIENT" + ); + } + + function _batchMint( + address to, + uint256[] memory ids, + uint256[] memory amounts, + bytes memory data + ) internal { + uint256 idsLength = ids.length; // Saves MLOADs. + + require(idsLength == amounts.length, "LENGTH_MISMATCH"); + + for (uint256 i = 0; i < idsLength; ) { + balanceOf[to][ids[i]] += amounts[i]; + + // An array can't have a total length + // larger than the max uint256 value. + unchecked { + ++i; + } + } + + emit TransferBatch(msg.sender, address(0), to, ids, amounts); + + require( + to.code.length == 0 + ? to != address(0) + : ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, address(0), ids, amounts, data) == + ERC1155TokenReceiver.onERC1155BatchReceived.selector, + "UNSAFE_RECIPIENT" + ); + } + + function _batchBurn( + address from, + uint256[] memory ids, + uint256[] memory amounts + ) internal { + uint256 idsLength = ids.length; // Saves MLOADs. + + require(idsLength == amounts.length, "LENGTH_MISMATCH"); + + for (uint256 i = 0; i < idsLength; ) { + balanceOf[from][ids[i]] -= amounts[i]; + + // An array can't have a total length + // larger than the max uint256 value. + unchecked { + ++i; + } + } + + emit TransferBatch(msg.sender, from, address(0), ids, amounts); + } + + function _burn( + address from, + uint256 id, + uint256 amount + ) internal { + balanceOf[from][id] -= amount; + + emit TransferSingle(msg.sender, from, address(0), id, amount); + } +} + +/// @notice A generic interface for a contract which properly accepts ERC1155 tokens. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol) +interface ERC1155TokenReceiver { + function onERC1155Received( + address operator, + address from, + uint256 id, + uint256 amount, + bytes calldata data + ) external returns (bytes4); + + function onERC1155BatchReceived( + address operator, + address from, + uint256[] calldata ids, + uint256[] calldata amounts, + bytes calldata data + ) external returns (bytes4); +} diff --git a/contracts/lib/solmate/src/tokens/ERC20.sol b/contracts/lib/solmate/src/tokens/ERC20.sol new file mode 100644 index 0000000000..ea89a35d62 --- /dev/null +++ b/contracts/lib/solmate/src/tokens/ERC20.sol @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.0; + +/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol) +/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) +/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. +abstract contract ERC20 { + /*/////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event Transfer(address indexed from, address indexed to, uint256 amount); + + event Approval(address indexed owner, address indexed spender, uint256 amount); + + /*/////////////////////////////////////////////////////////////// + METADATA STORAGE + //////////////////////////////////////////////////////////////*/ + + string public name; + + string public symbol; + + uint8 public immutable decimals; + + /*/////////////////////////////////////////////////////////////// + ERC20 STORAGE + //////////////////////////////////////////////////////////////*/ + + uint256 public totalSupply; + + mapping(address => uint256) public balanceOf; + + mapping(address => mapping(address => uint256)) public allowance; + + /*/////////////////////////////////////////////////////////////// + EIP-2612 STORAGE + //////////////////////////////////////////////////////////////*/ + + uint256 internal immutable INITIAL_CHAIN_ID; + + bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; + + mapping(address => uint256) public nonces; + + /*/////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor( + string memory _name, + string memory _symbol, + uint8 _decimals + ) { + name = _name; + symbol = _symbol; + decimals = _decimals; + + INITIAL_CHAIN_ID = block.chainid; + INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); + } + + /*/////////////////////////////////////////////////////////////// + ERC20 LOGIC + //////////////////////////////////////////////////////////////*/ + + function approve(address spender, uint256 amount) public virtual returns (bool) { + allowance[msg.sender][spender] = amount; + + emit Approval(msg.sender, spender, amount); + + return true; + } + + function transfer(address to, uint256 amount) public virtual returns (bool) { + balanceOf[msg.sender] -= amount; + + // Cannot overflow because the sum of all user + // balances can't exceed the max uint256 value. + unchecked { + balanceOf[to] += amount; + } + + emit Transfer(msg.sender, to, amount); + + return true; + } + + function transferFrom( + address from, + address to, + uint256 amount + ) public virtual returns (bool) { + uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. + + if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; + + balanceOf[from] -= amount; + + // Cannot overflow because the sum of all user + // balances can't exceed the max uint256 value. + unchecked { + balanceOf[to] += amount; + } + + emit Transfer(from, to, amount); + + return true; + } + + /*/////////////////////////////////////////////////////////////// + EIP-2612 LOGIC + //////////////////////////////////////////////////////////////*/ + + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual { + require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); + + // Unchecked because the only math done is incrementing + // the owner's nonce which cannot realistically overflow. + unchecked { + address recoveredAddress = ecrecover( + keccak256( + abi.encodePacked( + "\x19\x01", + DOMAIN_SEPARATOR(), + keccak256( + abi.encode( + keccak256( + "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" + ), + owner, + spender, + value, + nonces[owner]++, + deadline + ) + ) + ) + ), + v, + r, + s + ); + + require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); + + allowance[recoveredAddress][spender] = value; + } + + emit Approval(owner, spender, value); + } + + function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { + return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); + } + + function computeDomainSeparator() internal view virtual returns (bytes32) { + return + keccak256( + abi.encode( + keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), + keccak256(bytes(name)), + keccak256("1"), + block.chainid, + address(this) + ) + ); + } + + /*/////////////////////////////////////////////////////////////// + INTERNAL MINT/BURN LOGIC + //////////////////////////////////////////////////////////////*/ + + function _mint(address to, uint256 amount) internal virtual { + totalSupply += amount; + + // Cannot overflow because the sum of all user + // balances can't exceed the max uint256 value. + unchecked { + balanceOf[to] += amount; + } + + emit Transfer(address(0), to, amount); + } + + function _burn(address from, uint256 amount) internal virtual { + balanceOf[from] -= amount; + + // Cannot underflow because a user's balance + // will never be larger than the total supply. + unchecked { + totalSupply -= amount; + } + + emit Transfer(from, address(0), amount); + } +} diff --git a/contracts/lib/solmate/src/tokens/ERC721.sol b/contracts/lib/solmate/src/tokens/ERC721.sol new file mode 100644 index 0000000000..1c3d838493 --- /dev/null +++ b/contracts/lib/solmate/src/tokens/ERC721.sol @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.0; + +/// @notice Modern, minimalist, and gas efficient ERC-721 implementation. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol) +/// @dev Note that balanceOf does not revert if passed the zero address, in defiance of the ERC. +abstract contract ERC721 { + /*/////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + + event Transfer(address indexed from, address indexed to, uint256 indexed id); + + event Approval(address indexed owner, address indexed spender, uint256 indexed id); + + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + + /*/////////////////////////////////////////////////////////////// + METADATA STORAGE/LOGIC + //////////////////////////////////////////////////////////////*/ + + string public name; + + string public symbol; + + function tokenURI(uint256 id) public view virtual returns (string memory); + + /*/////////////////////////////////////////////////////////////// + ERC721 STORAGE + //////////////////////////////////////////////////////////////*/ + + mapping(address => uint256) public balanceOf; + + mapping(uint256 => address) public ownerOf; + + mapping(uint256 => address) public getApproved; + + mapping(address => mapping(address => bool)) public isApprovedForAll; + + /*/////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor(string memory _name, string memory _symbol) { + name = _name; + symbol = _symbol; + } + + /*/////////////////////////////////////////////////////////////// + ERC721 LOGIC + //////////////////////////////////////////////////////////////*/ + + function approve(address spender, uint256 id) public virtual { + address owner = ownerOf[id]; + + require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED"); + + getApproved[id] = spender; + + emit Approval(owner, spender, id); + } + + function setApprovalForAll(address operator, bool approved) public virtual { + isApprovedForAll[msg.sender][operator] = approved; + + emit ApprovalForAll(msg.sender, operator, approved); + } + + function transferFrom( + address from, + address to, + uint256 id + ) public virtual { + require(from == ownerOf[id], "WRONG_FROM"); + + require(to != address(0), "INVALID_RECIPIENT"); + + require( + msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id], + "NOT_AUTHORIZED" + ); + + // Underflow of the sender's balance is impossible because we check for + // ownership above and the recipient's balance can't realistically overflow. + unchecked { + balanceOf[from]--; + + balanceOf[to]++; + } + + ownerOf[id] = to; + + delete getApproved[id]; + + emit Transfer(from, to, id); + } + + function safeTransferFrom( + address from, + address to, + uint256 id + ) public virtual { + transferFrom(from, to, id); + + require( + to.code.length == 0 || + ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") == + ERC721TokenReceiver.onERC721Received.selector, + "UNSAFE_RECIPIENT" + ); + } + + function safeTransferFrom( + address from, + address to, + uint256 id, + bytes memory data + ) public virtual { + transferFrom(from, to, id); + + require( + to.code.length == 0 || + ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) == + ERC721TokenReceiver.onERC721Received.selector, + "UNSAFE_RECIPIENT" + ); + } + + /*/////////////////////////////////////////////////////////////// + ERC165 LOGIC + //////////////////////////////////////////////////////////////*/ + + function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { + return + interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 + interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721 + interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata + } + + /*/////////////////////////////////////////////////////////////// + INTERNAL MINT/BURN LOGIC + //////////////////////////////////////////////////////////////*/ + + function _mint(address to, uint256 id) internal virtual { + require(to != address(0), "INVALID_RECIPIENT"); + + require(ownerOf[id] == address(0), "ALREADY_MINTED"); + + // Counter overflow is incredibly unrealistic. + unchecked { + balanceOf[to]++; + } + + ownerOf[id] = to; + + emit Transfer(address(0), to, id); + } + + function _burn(uint256 id) internal virtual { + address owner = ownerOf[id]; + + require(owner != address(0), "NOT_MINTED"); + + // Ownership check above ensures no underflow. + unchecked { + balanceOf[owner]--; + } + + delete ownerOf[id]; + + delete getApproved[id]; + + emit Transfer(owner, address(0), id); + } + + /*/////////////////////////////////////////////////////////////// + INTERNAL SAFE MINT LOGIC + //////////////////////////////////////////////////////////////*/ + + function _safeMint(address to, uint256 id) internal virtual { + _mint(to, id); + + require( + to.code.length == 0 || + ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") == + ERC721TokenReceiver.onERC721Received.selector, + "UNSAFE_RECIPIENT" + ); + } + + function _safeMint( + address to, + uint256 id, + bytes memory data + ) internal virtual { + _mint(to, id); + + require( + to.code.length == 0 || + ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) == + ERC721TokenReceiver.onERC721Received.selector, + "UNSAFE_RECIPIENT" + ); + } +} + +/// @notice A generic interface for a contract which properly accepts ERC721 tokens. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol) +interface ERC721TokenReceiver { + function onERC721Received( + address operator, + address from, + uint256 id, + bytes calldata data + ) external returns (bytes4); +} diff --git a/contracts/lib/solmate/src/tokens/WETH.sol b/contracts/lib/solmate/src/tokens/WETH.sol new file mode 100644 index 0000000000..5c470e37bd --- /dev/null +++ b/contracts/lib/solmate/src/tokens/WETH.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.0; + +import {ERC20} from "./ERC20.sol"; + +import {SafeTransferLib} from "../utils/SafeTransferLib.sol"; + +/// @notice Minimalist and modern Wrapped Ether implementation. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/WETH.sol) +/// @author Inspired by WETH9 (https://github.com/dapphub/ds-weth/blob/master/src/weth9.sol) +contract WETH is ERC20("Wrapped Ether", "WETH", 18) { + using SafeTransferLib for address; + + event Deposit(address indexed from, uint256 amount); + + event Withdrawal(address indexed to, uint256 amount); + + function deposit() public payable virtual { + _mint(msg.sender, msg.value); + + emit Deposit(msg.sender, msg.value); + } + + function withdraw(uint256 amount) public virtual { + _burn(msg.sender, amount); + + emit Withdrawal(msg.sender, amount); + + msg.sender.safeTransferETH(amount); + } + + receive() external payable virtual { + deposit(); + } +} diff --git a/contracts/lib/solmate/src/utils/Bytes32AddressLib.sol b/contracts/lib/solmate/src/utils/Bytes32AddressLib.sol new file mode 100644 index 0000000000..bc857be105 --- /dev/null +++ b/contracts/lib/solmate/src/utils/Bytes32AddressLib.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.0; + +/// @notice Library for converting between addresses and bytes32 values. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/Bytes32AddressLib.sol) +library Bytes32AddressLib { + function fromLast20Bytes(bytes32 bytesValue) internal pure returns (address) { + return address(uint160(uint256(bytesValue))); + } + + function fillLast12Bytes(address addressValue) internal pure returns (bytes32) { + return bytes32(bytes20(addressValue)); + } +} diff --git a/contracts/lib/solmate/src/utils/CREATE3.sol b/contracts/lib/solmate/src/utils/CREATE3.sol new file mode 100644 index 0000000000..04e091556f --- /dev/null +++ b/contracts/lib/solmate/src/utils/CREATE3.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.0; + +import {Bytes32AddressLib} from "./Bytes32AddressLib.sol"; + +/// @notice Deploy to deterministic addresses without an initcode factor. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/CREATE3.sol) +/// @author Modified from 0xSequence (https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol) +library CREATE3 { + using Bytes32AddressLib for bytes32; + + //--------------------------------------------------------------------------------// + // Opcode | Opcode + Arguments | Description | Stack View // + //--------------------------------------------------------------------------------// + // 0x36 | 0x36 | CALLDATASIZE | size // + // 0x3d | 0x3d | RETURNDATASIZE | 0 size // + // 0x3d | 0x3d | RETURNDATASIZE | 0 0 size // + // 0x37 | 0x37 | CALLDATACOPY | // + // 0x36 | 0x36 | CALLDATASIZE | size // + // 0x3d | 0x3d | RETURNDATASIZE | 0 size // + // 0x34 | 0x34 | CALLVALUE | value 0 size // + // 0xf0 | 0xf0 | CREATE | newContract // + //--------------------------------------------------------------------------------// + // Opcode | Opcode + Arguments | Description | Stack View // + //--------------------------------------------------------------------------------// + // 0x67 | 0x67XXXXXXXXXXXXXXXX | PUSH8 bytecode | bytecode // + // 0x3d | 0x3d | RETURNDATASIZE | 0 bytecode // + // 0x52 | 0x52 | MSTORE | // + // 0x60 | 0x6008 | PUSH1 08 | 8 // + // 0x60 | 0x6018 | PUSH1 18 | 24 8 // + // 0xf3 | 0xf3 | RETURN | // + //--------------------------------------------------------------------------------// + bytes internal constant PROXY_BYTECODE = hex"67_36_3d_3d_37_36_3d_34_f0_3d_52_60_08_60_18_f3"; + + bytes32 internal constant PROXY_BYTECODE_HASH = keccak256(PROXY_BYTECODE); + + function deploy( + bytes32 salt, + bytes memory creationCode, + uint256 value + ) internal returns (address deployed) { + bytes memory proxyChildBytecode = PROXY_BYTECODE; + + address proxy; + assembly { + // Deploy a new contract with our pre-made bytecode via CREATE2. + // We start 32 bytes into the code to avoid copying the byte length. + proxy := create2(0, add(proxyChildBytecode, 32), mload(proxyChildBytecode), salt) + } + require(proxy != address(0), "DEPLOYMENT_FAILED"); + + deployed = getDeployed(salt); + (bool success, ) = proxy.call{value: value}(creationCode); + require(success && deployed.code.length != 0, "INITIALIZATION_FAILED"); + } + + function getDeployed(bytes32 salt) internal view returns (address) { + address proxy = keccak256( + abi.encodePacked( + // Prefix: + bytes1(0xFF), + // Creator: + address(this), + // Salt: + salt, + // Bytecode hash: + PROXY_BYTECODE_HASH + ) + ).fromLast20Bytes(); + + return + keccak256( + abi.encodePacked( + // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01) + // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex) + hex"d6_94", + proxy, + hex"01" // Nonce of the proxy contract (1) + ) + ).fromLast20Bytes(); + } +} diff --git a/contracts/lib/solmate/src/utils/FixedPointMathLib.sol b/contracts/lib/solmate/src/utils/FixedPointMathLib.sol new file mode 100644 index 0000000000..25bb9370b5 --- /dev/null +++ b/contracts/lib/solmate/src/utils/FixedPointMathLib.sol @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.0; + +/// @notice Arithmetic library with operations for fixed-point numbers. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/FixedPointMathLib.sol) +/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol) +library FixedPointMathLib { + /*/////////////////////////////////////////////////////////////// + SIMPLIFIED FIXED POINT OPERATIONS + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s. + + function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) { + return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down. + } + + function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) { + return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up. + } + + function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) { + return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down. + } + + function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) { + return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up. + } + + /*/////////////////////////////////////////////////////////////// + LOW LEVEL FIXED POINT OPERATIONS + //////////////////////////////////////////////////////////////*/ + + function mulDivDown( + uint256 x, + uint256 y, + uint256 denominator + ) internal pure returns (uint256 z) { + assembly { + // Store x * y in z for now. + z := mul(x, y) + + // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y)) + if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) { + revert(0, 0) + } + + // Divide z by the denominator. + z := div(z, denominator) + } + } + + function mulDivUp( + uint256 x, + uint256 y, + uint256 denominator + ) internal pure returns (uint256 z) { + assembly { + // Store x * y in z for now. + z := mul(x, y) + + // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y)) + if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) { + revert(0, 0) + } + + // First, divide z - 1 by the denominator and add 1. + // We allow z - 1 to underflow if z is 0, because we multiply the + // end result by 0 if z is zero, ensuring we return 0 if z is zero. + z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1)) + } + } + + function rpow( + uint256 x, + uint256 n, + uint256 scalar + ) internal pure returns (uint256 z) { + assembly { + switch x + case 0 { + switch n + case 0 { + // 0 ** 0 = 1 + z := scalar + } + default { + // 0 ** n = 0 + z := 0 + } + } + default { + switch mod(n, 2) + case 0 { + // If n is even, store scalar in z for now. + z := scalar + } + default { + // If n is odd, store x in z for now. + z := x + } + + // Shifting right by 1 is like dividing by 2. + let half := shr(1, scalar) + + for { + // Shift n right by 1 before looping to halve it. + n := shr(1, n) + } n { + // Shift n right by 1 each iteration to halve it. + n := shr(1, n) + } { + // Revert immediately if x ** 2 would overflow. + // Equivalent to iszero(eq(div(xx, x), x)) here. + if shr(128, x) { + revert(0, 0) + } + + // Store x squared. + let xx := mul(x, x) + + // Round to the nearest number. + let xxRound := add(xx, half) + + // Revert if xx + half overflowed. + if lt(xxRound, xx) { + revert(0, 0) + } + + // Set x to scaled xxRound. + x := div(xxRound, scalar) + + // If n is even: + if mod(n, 2) { + // Compute z * x. + let zx := mul(z, x) + + // If z * x overflowed: + if iszero(eq(div(zx, x), z)) { + // Revert if x is non-zero. + if iszero(iszero(x)) { + revert(0, 0) + } + } + + // Round to the nearest number. + let zxRound := add(zx, half) + + // Revert if zx + half overflowed. + if lt(zxRound, zx) { + revert(0, 0) + } + + // Return properly scaled zxRound. + z := div(zxRound, scalar) + } + } + } + } + } + + /*/////////////////////////////////////////////////////////////// + GENERAL NUMBER UTILITIES + //////////////////////////////////////////////////////////////*/ + + function sqrt(uint256 x) internal pure returns (uint256 z) { + assembly { + // Start off with z at 1. + z := 1 + + // Used below to help find a nearby power of 2. + let y := x + + // Find the lowest power of 2 that is at least sqrt(x). + if iszero(lt(y, 0x100000000000000000000000000000000)) { + y := shr(128, y) // Like dividing by 2 ** 128. + z := shl(64, z) // Like multiplying by 2 ** 64. + } + if iszero(lt(y, 0x10000000000000000)) { + y := shr(64, y) // Like dividing by 2 ** 64. + z := shl(32, z) // Like multiplying by 2 ** 32. + } + if iszero(lt(y, 0x100000000)) { + y := shr(32, y) // Like dividing by 2 ** 32. + z := shl(16, z) // Like multiplying by 2 ** 16. + } + if iszero(lt(y, 0x10000)) { + y := shr(16, y) // Like dividing by 2 ** 16. + z := shl(8, z) // Like multiplying by 2 ** 8. + } + if iszero(lt(y, 0x100)) { + y := shr(8, y) // Like dividing by 2 ** 8. + z := shl(4, z) // Like multiplying by 2 ** 4. + } + if iszero(lt(y, 0x10)) { + y := shr(4, y) // Like dividing by 2 ** 4. + z := shl(2, z) // Like multiplying by 2 ** 2. + } + if iszero(lt(y, 0x8)) { + // Equivalent to 2 ** z. + z := shl(1, z) + } + + // Shifting right by 1 is like dividing by 2. + z := shr(1, add(z, div(x, z))) + z := shr(1, add(z, div(x, z))) + z := shr(1, add(z, div(x, z))) + z := shr(1, add(z, div(x, z))) + z := shr(1, add(z, div(x, z))) + z := shr(1, add(z, div(x, z))) + z := shr(1, add(z, div(x, z))) + + // Compute a rounded down version of z. + let zRoundDown := div(x, z) + + // If zRoundDown is smaller, use it. + if lt(zRoundDown, z) { + z := zRoundDown + } + } + } +} diff --git a/contracts/lib/solmate/src/utils/ReentrancyGuard.sol b/contracts/lib/solmate/src/utils/ReentrancyGuard.sol new file mode 100644 index 0000000000..9686ca228d --- /dev/null +++ b/contracts/lib/solmate/src/utils/ReentrancyGuard.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.0; + +/// @notice Gas optimized reentrancy protection for smart contracts. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ReentrancyGuard.sol) +/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol) +abstract contract ReentrancyGuard { + uint256 private locked = 1; + + modifier nonReentrant() { + require(locked == 1, "REENTRANCY"); + + locked = 2; + + _; + + locked = 1; + } +} diff --git a/contracts/lib/solmate/src/utils/SSTORE2.sol b/contracts/lib/solmate/src/utils/SSTORE2.sol new file mode 100644 index 0000000000..265f4a56ba --- /dev/null +++ b/contracts/lib/solmate/src/utils/SSTORE2.sol @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.0; + +/// @notice Read and write to persistent storage at a fraction of the cost. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SSTORE2.sol) +/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol) +library SSTORE2 { + uint256 internal constant DATA_OFFSET = 1; // We skip the first byte as it's a STOP opcode to ensure the contract can't be called. + + /*/////////////////////////////////////////////////////////////// + WRITE LOGIC + //////////////////////////////////////////////////////////////*/ + + function write(bytes memory data) internal returns (address pointer) { + // Prefix the bytecode with a STOP opcode to ensure it cannot be called. + bytes memory runtimeCode = abi.encodePacked(hex"00", data); + + bytes memory creationCode = abi.encodePacked( + //---------------------------------------------------------------------------------------------------------------// + // Opcode | Opcode + Arguments | Description | Stack View // + //---------------------------------------------------------------------------------------------------------------// + // 0x60 | 0x600B | PUSH1 11 | codeOffset // + // 0x59 | 0x59 | MSIZE | 0 codeOffset // + // 0x81 | 0x81 | DUP2 | codeOffset 0 codeOffset // + // 0x38 | 0x38 | CODESIZE | codeSize codeOffset 0 codeOffset // + // 0x03 | 0x03 | SUB | (codeSize - codeOffset) 0 codeOffset // + // 0x80 | 0x80 | DUP | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset // + // 0x92 | 0x92 | SWAP3 | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) // + // 0x59 | 0x59 | MSIZE | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) // + // 0x39 | 0x39 | CODECOPY | 0 (codeSize - codeOffset) // + // 0xf3 | 0xf3 | RETURN | // + //---------------------------------------------------------------------------------------------------------------// + hex"60_0B_59_81_38_03_80_92_59_39_F3", // Returns all code in the contract except for the first 11 (0B in hex) bytes. + runtimeCode // The bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit. + ); + + assembly { + // Deploy a new contract with the generated creation code. + // We start 32 bytes into the code to avoid copying the byte length. + pointer := create(0, add(creationCode, 32), mload(creationCode)) + } + + require(pointer != address(0), "DEPLOYMENT_FAILED"); + } + + /*/////////////////////////////////////////////////////////////// + READ LOGIC + //////////////////////////////////////////////////////////////*/ + + function read(address pointer) internal view returns (bytes memory) { + return readBytecode(pointer, DATA_OFFSET, pointer.code.length - DATA_OFFSET); + } + + function read(address pointer, uint256 start) internal view returns (bytes memory) { + start += DATA_OFFSET; + + return readBytecode(pointer, start, pointer.code.length - start); + } + + function read( + address pointer, + uint256 start, + uint256 end + ) internal view returns (bytes memory) { + start += DATA_OFFSET; + end += DATA_OFFSET; + + require(pointer.code.length >= end, "OUT_OF_BOUNDS"); + + return readBytecode(pointer, start, end - start); + } + + /*/////////////////////////////////////////////////////////////// + INTERNAL HELPER LOGIC + //////////////////////////////////////////////////////////////*/ + + function readBytecode( + address pointer, + uint256 start, + uint256 size + ) private view returns (bytes memory data) { + assembly { + // Get a pointer to some free memory. + data := mload(0x40) + + // Update the free memory pointer to prevent overriding our data. + // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)). + // Adding 31 to size and running the result through the logic above ensures + // the memory pointer remains word-aligned, following the Solidity convention. + mstore(0x40, add(data, and(add(add(size, 32), 31), not(31)))) + + // Store the size of the data in the first 32 byte chunk of free memory. + mstore(data, size) + + // Copy the code into memory right after the 32 bytes we used to store the size. + extcodecopy(pointer, add(data, 32), start, size) + } + } +} diff --git a/contracts/lib/solmate/src/utils/SafeCastLib.sol b/contracts/lib/solmate/src/utils/SafeCastLib.sol new file mode 100644 index 0000000000..ab4287f729 --- /dev/null +++ b/contracts/lib/solmate/src/utils/SafeCastLib.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.0; + +/// @notice Safe unsigned integer casting library that reverts on overflow. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeCastLib.sol) +/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol) +library SafeCastLib { + function safeCastTo248(uint256 x) internal pure returns (uint248 y) { + require(x < 1 << 248); + + y = uint248(x); + } + + function safeCastTo224(uint256 x) internal pure returns (uint224 y) { + require(x < 1 << 224); + + y = uint224(x); + } + + function safeCastTo192(uint256 x) internal pure returns (uint192 y) { + require(x < 1 << 192); + + y = uint192(x); + } + + function safeCastTo160(uint256 x) internal pure returns (uint160 y) { + require(x < 1 << 160); + + y = uint160(x); + } + + function safeCastTo128(uint256 x) internal pure returns (uint128 y) { + require(x < 1 << 128); + + y = uint128(x); + } + + function safeCastTo96(uint256 x) internal pure returns (uint96 y) { + require(x < 1 << 96); + + y = uint96(x); + } + + function safeCastTo64(uint256 x) internal pure returns (uint64 y) { + require(x < 1 << 64); + + y = uint64(x); + } + + function safeCastTo32(uint256 x) internal pure returns (uint32 y) { + require(x < 1 << 32); + + y = uint32(x); + } + + function safeCastTo8(uint256 x) internal pure returns (uint8 y) { + require(x < 1 << 8); + + y = uint8(x); + } +} diff --git a/contracts/lib/solmate/src/utils/SafeTransferLib.sol b/contracts/lib/solmate/src/utils/SafeTransferLib.sol new file mode 100644 index 0000000000..888376c40b --- /dev/null +++ b/contracts/lib/solmate/src/utils/SafeTransferLib.sol @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity >=0.8.0; + +import {ERC20} from "../tokens/ERC20.sol"; + +/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. +/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol) +/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. +/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. +library SafeTransferLib { + event Debug(bool one, bool two, uint256 retsize); + + /*/////////////////////////////////////////////////////////////// + ETH OPERATIONS + //////////////////////////////////////////////////////////////*/ + + function safeTransferETH(address to, uint256 amount) internal { + bool success; + + assembly { + // Transfer the ETH and store if it succeeded or not. + success := call(gas(), to, amount, 0, 0, 0, 0) + } + + require(success, "ETH_TRANSFER_FAILED"); + } + + /*/////////////////////////////////////////////////////////////// + ERC20 OPERATIONS + //////////////////////////////////////////////////////////////*/ + + function safeTransferFrom( + ERC20 token, + address from, + address to, + uint256 amount + ) internal { + bool success; + + assembly { + // Get a pointer to some free memory. + let freeMemoryPointer := mload(0x40) + + // Write the abi-encoded calldata into memory, beginning with the function selector. + mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) + mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument. + mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument. + mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. + + success := and( + // Set success to whether the call reverted, if not we check it either + // returned exactly 1 (can't just be non-zero data), or had no return data. + or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), + // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. + // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. + // Counterintuitively, this call must be positioned second to the or() call in the + // surrounding and() call or else returndatasize() will be zero during the computation. + call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) + ) + } + + require(success, "TRANSFER_FROM_FAILED"); + } + + function safeTransfer( + ERC20 token, + address to, + uint256 amount + ) internal { + bool success; + + assembly { + // Get a pointer to some free memory. + let freeMemoryPointer := mload(0x40) + + // Write the abi-encoded calldata into memory, beginning with the function selector. + mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) + mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. + mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. + + success := and( + // Set success to whether the call reverted, if not we check it either + // returned exactly 1 (can't just be non-zero data), or had no return data. + or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), + // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. + // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. + // Counterintuitively, this call must be positioned second to the or() call in the + // surrounding and() call or else returndatasize() will be zero during the computation. + call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) + ) + } + + require(success, "TRANSFER_FAILED"); + } + + function safeApprove( + ERC20 token, + address to, + uint256 amount + ) internal { + bool success; + + assembly { + // Get a pointer to some free memory. + let freeMemoryPointer := mload(0x40) + + // Write the abi-encoded calldata into memory, beginning with the function selector. + mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) + mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. + mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. + + success := and( + // Set success to whether the call reverted, if not we check it either + // returned exactly 1 (can't just be non-zero data), or had no return data. + or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), + // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. + // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. + // Counterintuitively, this call must be positioned second to the or() call in the + // surrounding and() call or else returndatasize() will be zero during the computation. + call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) + ) + } + + require(success, "APPROVE_FAILED"); + } +} diff --git a/contracts/test/_fixture.js b/contracts/test/_fixture.js index fd82eb5604..818a02fbc2 100644 --- a/contracts/test/_fixture.js +++ b/contracts/test/_fixture.js @@ -38,6 +38,7 @@ async function defaultFixture() { ); const dripperProxy = await ethers.getContract("DripperProxy"); const dripper = await ethers.getContractAt("Dripper", dripperProxy.address); + const wousd = await ethers.getContract("WrappedOusd"); const governorContract = await ethers.getContract("Governor"); const CompoundStrategyFactory = await ethers.getContractFactory( "CompoundStrategy" @@ -304,6 +305,7 @@ async function defaultFixture() { compensationClaims, flipper, buyback, + wousd, }; } diff --git a/contracts/test/token.js b/contracts/test/token/ousd.js similarity index 99% rename from contracts/test/token.js rename to contracts/test/token/ousd.js index d9c8d4cfcb..0030e33549 100644 --- a/contracts/test/token.js +++ b/contracts/test/token/ousd.js @@ -1,5 +1,5 @@ const { expect } = require("chai"); -const { defaultFixture } = require("./_fixture"); +const { defaultFixture } = require("../_fixture"); const { utils } = require("ethers"); const { @@ -8,7 +8,7 @@ const { usdcUnits, isFork, loadFixture, -} = require("./helpers"); +} = require("../helpers"); describe("Token", function () { if (isFork) { diff --git a/contracts/test/token/wousd.js b/contracts/test/token/wousd.js new file mode 100644 index 0000000000..0242218fed --- /dev/null +++ b/contracts/test/token/wousd.js @@ -0,0 +1,78 @@ +const { expect } = require("chai"); +const { defaultFixture } = require("../_fixture"); + +const { ousdUnits, daiUnits, isFork, loadFixture } = require("../helpers"); + +describe("WOUSD", function () { + if (isFork) { + this.timeout(0); + } + + let ousd, wousd, dai, matt, josh, governor; + + beforeEach(async () => { + const fixture = await loadFixture(defaultFixture); + ousd = fixture.ousd; + wousd = fixture.wousd; + dai = fixture.dai; + matt = fixture.matt; + josh = fixture.josh; + governor = fixture.governor; + + // Josh wraps 50 + await ousd.connect(josh).approve(wousd.address, ousdUnits("1000")); + await wousd.connect(josh).deposit(ousdUnits("50"), josh.address); + // Matt gives money to wOUSD, which counts as yield and changes the effective price of WOUSD + // 1 WOUSD will be worth 2 OUSD + await ousd.connect(matt).transfer(wousd.address, ousdUnits("50")); + }); + + describe("Funds in, Funds out", async () => { + it("should deposit at the correct ratio", async () => { + await wousd.connect(josh).deposit(ousdUnits("50"), josh.address); + await expect(josh).to.have.a.balanceOf("75", wousd); + await expect(josh).to.have.a.balanceOf("0", ousd); + }); + it("should withdraw at the correct ratio", async () => { + await wousd + .connect(josh) + .withdraw(ousdUnits("50"), josh.address, josh.address); + await expect(josh).to.have.a.balanceOf("25", wousd); + await expect(josh).to.have.a.balanceOf("100", ousd); + }); + it("should mint at the correct ratio", async () => { + await wousd.connect(josh).mint(ousdUnits("25"), josh.address); + await expect(josh).to.have.a.balanceOf("75", wousd); + await expect(josh).to.have.a.balanceOf("0", ousd); + }); + it("should redeem at the correct ratio", async () => { + await expect(josh).to.have.a.balanceOf("50", wousd); + await wousd + .connect(josh) + .redeem(ousdUnits("50"), josh.address, josh.address); + await expect(josh).to.have.a.balanceOf("0", wousd); + await expect(josh).to.have.a.balanceOf("150", ousd); + }); + }); + + describe("Token recovery", async () => { + it("should allow a governor to recover tokens", async () => { + await dai.connect(matt).transfer(wousd.address, daiUnits("2")); + await expect(wousd).to.have.a.balanceOf("2", dai); + await expect(governor).to.have.a.balanceOf("1000", dai); + await wousd.connect(governor).transferToken(dai.address, daiUnits("2")); + await expect(wousd).to.have.a.balanceOf("0", dai); + await expect(governor).to.have.a.balanceOf("1002", dai); + }); + it("should not allow a governor to collect OUSD", async () => { + await expect( + wousd.connect(governor).transferToken(ousd.address, ousdUnits("2")) + ).to.be.revertedWith("Cannot collect OUSD"); + }); + it("should not allow a non governor to recover tokens ", async () => { + await expect( + wousd.connect(josh).transferToken(ousd.address, ousdUnits("2")) + ).to.be.revertedWith("Caller is not the Governor"); + }); + }); +}); From ab2114ed2503571b178b65ce9de28e2095f14f32 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Fri, 25 Mar 2022 14:30:37 -0400 Subject: [PATCH 02/13] Remove extra solmate things. --- contracts/lib/solmate/.dapprc | 14 -- contracts/lib/solmate/.gas-snapshot | 215 ------------------ .../solmate/.github/pull_request_template.md | 14 -- .../solmate/.github/workflows/dapp-tests.yml | 27 --- .../solmate/.github/workflows/forge-tests.yml | 20 -- contracts/lib/solmate/.gitmodules | 3 - contracts/lib/solmate/.vscode/settings.json | 10 - 7 files changed, 303 deletions(-) delete mode 100644 contracts/lib/solmate/.dapprc delete mode 100644 contracts/lib/solmate/.gas-snapshot delete mode 100644 contracts/lib/solmate/.github/pull_request_template.md delete mode 100644 contracts/lib/solmate/.github/workflows/dapp-tests.yml delete mode 100644 contracts/lib/solmate/.github/workflows/forge-tests.yml delete mode 100644 contracts/lib/solmate/.gitmodules delete mode 100644 contracts/lib/solmate/.vscode/settings.json diff --git a/contracts/lib/solmate/.dapprc b/contracts/lib/solmate/.dapprc deleted file mode 100644 index e5524b4b72..0000000000 --- a/contracts/lib/solmate/.dapprc +++ /dev/null @@ -1,14 +0,0 @@ -# Basic build/test configuration. -export DAPP_SOLC_VERSION=0.8.10 -export DAPP_BUILD_OPTIMIZE=1 -export DAPP_BUILD_OPTIMIZE_RUNS=1000000 -export DAPP_LINK_TEST_LIBRARIES=0 -export DAPP_TEST_VERBOSITY=1 -export DAPP_TEST_SMTTIMEOUT=500000 - -if [ "$DEEP_FUZZ" = "true" ] -then - export DAPP_TEST_FUZZ_RUNS=10000 # Fuzz for a long time if DEEP_FUZZ is set to true. -else - export DAPP_TEST_FUZZ_RUNS=100 # Only fuzz briefly if DEEP_FUZZ is not set to true. -fi diff --git a/contracts/lib/solmate/.gas-snapshot b/contracts/lib/solmate/.gas-snapshot deleted file mode 100644 index 73bea51220..0000000000 --- a/contracts/lib/solmate/.gas-snapshot +++ /dev/null @@ -1,215 +0,0 @@ -testFailSetAuthorityWithRestrictiveAuthority() (gas: 126002) -testSetAuthorityWithPermissiveAuthority() (gas: 127687) -testFailSetOwnerWithRestrictiveAuthority() (gas: 126166) -testFailCallFunctionAsNonOwner() (gas: 4191) -testSetAuthorityAsOwner() (gas: 23802) -testFailCallFunctionAsOwnerWithOutOfOrderAuthority() (gas: 135733) -testCallFunctionWithPermissiveAuthority() (gas: 125973) -testFailSetAuthorityAsNonOwner() (gas: 6960) -testFailSetOwnerAsOwnerWithOutOfOrderAuthority() (gas: 135873) -testCallFunctionAsOwner() (gas: 21371) -testFailCallFunctionWithRestrictiveAuthority() (gas: 126125) -testSetOwnerWithPermissiveAuthority() (gas: 147508) -testFailSetOwnerAsNonOwner() (gas: 4309) -testSetAuthorityAsOwnerWithOutOfOrderAuthority() (gas: 234329) -testSetOwnerAsOwner() (gas: 3998) -testFromLast20Bytes() (gas: 191) -testFillLast12Bytes() (gas: 223) -testFailDoubleDeploySameBytecode() (gas: 277076930206699) -testDeployERC20() (gas: 860923) -testFailDoubleDeployDifferentBytecode() (gas: 277076930214644) -testFailBoundMinBiggerThanMax() (gas: 309) -testBound() (gas: 16755) -testFailSafeBatchTransferFromToRevertingERC1155Recipient() (gas: 1041059) -testMintToEOA() (gas: 30265) -testFailMintToNonERC155Recipient() (gas: 71897) -testFailSafeBatchTransferFromToZero() (gas: 805760) -testBatchMintToERC1155Recipient() (gas: 946375) -testApproveAll() (gas: 26509) -testFailSafeBatchTransferFromWithArrayLengthMismatch() (gas: 681042) -testFailBatchMintToZero() (gas: 127242) -testFailSafeBatchTransferFromToWrongReturnDataERC1155Recipient() (gas: 992983) -testSafeTransferFromToERC1155Recipient() (gas: 1210543) -testFailBatchMintToWrongReturnDataERC1155Recipient() (gas: 314473) -testFailBatchMintToRevertingERC1155Recipient() (gas: 362536) -testBatchBurn() (gas: 146591) -testFailBurnInsufficientBalance() (gas: 30352) -testFailSafeTransferFromToWrongReturnDataERC1155Recipient() (gas: 243471) -testFailMintToRevertingERC155Recipient() (gas: 263148) -testFailSafeBatchTransferFromToNonERC1155Recipient() (gas: 849517) -testFailSafeTransferFromInsufficientBalance() (gas: 579173) -testFailSafeTransferFromToNonERC155Recipient() (gas: 100376) -testFailBatchMintToNonERC1155Recipient() (gas: 171010) -testSafeBatchTransferFromToEOA() (gas: 817022) -testFailSafeTransferFromToRevertingERC1155Recipient() (gas: 291604) -testBatchMintToEOA() (gas: 132842) -testFailBatchBurnInsufficientBalance() (gas: 131673) -testSafeBatchTransferFromToERC1155Recipient() (gas: 1650404) -testFailBalanceOfBatchWithArrayMismatch() (gas: 4794) -testFailSafeBatchTransferInsufficientBalance() (gas: 682003) -testSafeTransferFromToEOA() (gas: 609087) -testMintToERC1155Recipient() (gas: 612041) -testFailBatchMintWithArrayMismatch() (gas: 5118) -testBatchBalanceOf() (gas: 153791) -testFailSafeTransferFromToZero() (gas: 57667) -testFailSafeTransferFromSelfInsufficientBalance() (gas: 29956) -testBurn() (gas: 34098) -testFailBatchBurnWithArrayLengthMismatch() (gas: 131065) -testFailMintToZero() (gas: 29205) -testSafeTransferFromSelf() (gas: 59828) -testFailMintToWrongReturnDataERC155Recipient() (gas: 263102) -testInfiniteApproveTransferFrom() (gas: 387818) -testApprove() (gas: 26558) -testTransferFrom() (gas: 388112) -testFailTransferFromInsufficientBalance() (gas: 359489) -testFailPermitPastDeadline() (gas: 1489) -testFailPermitReplay() (gas: 59085) -testMint() (gas: 49246) -testFailTransferFromInsufficientAllowance() (gas: 359013) -testTransfer() (gas: 75672) -testBurn() (gas: 52470) -testPermit() (gas: 55993) -testFailTransferInsufficientBalance() (gas: 48306) -testFailPermitBadDeadline() (gas: 29724) -testFailPermitBadNonce() (gas: 29674) -testFailRedeemWithNoShareAmount() (gas: 25839) -testFailRedeemWithNotEnoughShareAmount() (gas: 190632) -testFailWithdrawWithNoUnderlyingAmount() (gas: 25792) -testFailMintWithNoApproval() (gas: 6296) -testFailDepositWithNotEnoughApproval() (gas: 77987) -testFailRedeemZero() (gas: 3461) -testFailWithdrawWithNotEnoughUnderlyingAmount() (gas: 190615) -testFailDepositZero() (gas: 3274) -testMultipleMintDepositRedeemWithdraw() (gas: 1446573) -testWithdrawZero() (gas: 43465) -testFailDepositWithNoApproval() (gas: 6351) -testVaultInteractionsForSomeoneElse() (gas: 1287566) -testMintZero() (gas: 45598) -testSafeTransferFromToERC721Recipient() (gas: 885703) -testFailSafeMintToERC721RecipientWithWrongReturnDataWithData() (gas: 163605) -testApprove() (gas: 73904) -testFailBurnUnMinted() (gas: 3379) -testFailSafeTransferFromToERC721RecipientWithWrongReturnDataWithData() (gas: 191740) -testFailDoubleMint() (gas: 48808) -testApproveAll() (gas: 26585) -testFailApproveUnAuthorized() (gas: 51054) -testFailSafeTransferFromToRevertingERC721RecipientWithData() (gas: 237450) -testFailSafeMintToNonERC721RecipientWithData() (gas: 93740) -testFailTransferFromWrongFrom() (gas: 48838) -testFailSafeMintToRevertingERC721Recipient() (gas: 208477) -testTransferFrom() (gas: 530630) -testFailSafeMintToNonERC721Recipient() (gas: 92893) -testFailDoubleBurn() (gas: 54465) -testFailSafeMintToERC721RecipientWithWrongReturnData() (gas: 162744) -testFailSafeTransferFromToNonERC721Recipient() (gas: 121141) -testMint() (gas: 49778) -testFailApproveUnMinted() (gas: 5672) -testFailTransferFromToZero() (gas: 48903) -testSafeMintToERC721Recipient() (gas: 385391) -testSafeTransferFromToEOA() (gas: 533049) -testSafeMintToEOA() (gas: 52413) -testApproveBurn() (gas: 79785) -testFailSafeTransferFromToERC721RecipientWithWrongReturnData() (gas: 190988) -testTransferFromApproveAll() (gas: 530281) -testFailTransferFromUnOwned() (gas: 3500) -testFailSafeTransferFromToNonERC721RecipientWithData() (gas: 121921) -testBurn() (gas: 55481) -testFailSafeMintToRevertingERC721RecipientWithData() (gas: 209269) -testFailMintToZero() (gas: 1253) -testFailTransferFromNotOwner() (gas: 53372) -testSafeMintToERC721RecipientWithData() (gas: 406553) -testFailSafeTransferFromToRevertingERC721Recipient() (gas: 236721) -testSafeTransferFromToERC721RecipientWithData() (gas: 906865) -testTransferFromSelf() (gas: 80050) -testMulWadDown() (gas: 821) -testDivWadDownEdgeCases() (gas: 423) -testFailDivWadUpZeroDenominator() (gas: 342) -testDivWadUp() (gas: 981) -testMulWadDownEdgeCases() (gas: 886) -testFailMulDivUpZeroDenominator() (gas: 317) -testMulDivUpEdgeCases() (gas: 846) -testDivWadUpEdgeCases() (gas: 482) -testFailDivWadDownZeroDenominator() (gas: 362) -testRPow() (gas: 2142) -testMulDivDownEdgeCases() (gas: 751) -testSqrt() (gas: 2537) -testDivWadDown() (gas: 864) -testMulDivDown() (gas: 1861) -testMulWadUpEdgeCases() (gas: 1002) -testMulWadUp() (gas: 959) -testFailMulDivDownZeroDenominator() (gas: 316) -testMulDivUp() (gas: 2273) -testSetRoles() (gas: 33023) -testCanCallWithCustomAuthorityOverridesPublicCapability() (gas: 295417) -testCanCallPublicCapability() (gas: 39631) -testSetTargetCustomAuthority() (gas: 31736) -testCanCallWithCustomAuthorityOverridesUserWithRole() (gas: 334265) -testCanCallWithAuthorizedRole() (gas: 97461) -testSetRoleCapabilities() (gas: 32997) -testCanCallWithCustomAuthority() (gas: 466959) -testSetPublicCapabilities() (gas: 31468) -testNoReentrancy() (gas: 1015) -testProtectedCall() (gas: 23649) -testFailUnprotectedCall() (gas: 30515) -testSetRoles() (gas: 32998) -testCanCallPublicCapability() (gas: 38436) -testCanCallWithAuthorizedRole() (gas: 96267) -testSetRoleCapabilities() (gas: 34588) -testSetPublicCapabilities() (gas: 33244) -testWriteRead() (gas: 53511) -testWriteReadFullStartBound() (gas: 34725) -testFailWriteReadEmptyOutOfBounds() (gas: 34432) -testWriteReadFullBoundedRead() (gas: 53708) -testFailReadInvalidPointer() (gas: 2905) -testFailWriteReadOutOfStartBound() (gas: 34346) -testFailReadInvalidPointerCustomStartBound() (gas: 2982) -testWriteReadEmptyBound() (gas: 34639) -testFailWriteReadOutOfBounds() (gas: 34453) -testWriteReadCustomBounds() (gas: 34853) -testWriteReadCustomStartBound() (gas: 34768) -testFailReadInvalidPointerCustomBounds() (gas: 3143) -testSafeCastTo248() (gas: 427) -testSafeCastTo128() (gas: 449) -testSafeCastTo32() (gas: 471) -testFailSafeCastTo192() (gas: 344) -testSafeCastTo192() (gas: 471) -testFailSafeCastTo96() (gas: 321) -testSafeCastTo96() (gas: 469) -testSafeCastTo224() (gas: 491) -testFailSafeCastTo8() (gas: 296) -testFailSafeCastTo64() (gas: 321) -testSafeCastTo64() (gas: 470) -testFailSafeCastTo248() (gas: 365) -testFailSafeCastTo224() (gas: 343) -testFailSafeCastTo128() (gas: 321) -testSafeCastTo160() (gas: 470) -testFailSafeCastTo160() (gas: 342) -testFailSafeCastTo32() (gas: 364) -testSafeCastTo8() (gas: 469) -testFailTransferWithReturnsFalse() (gas: 4032) -testFailTransferFromWithReverting() (gas: 5045) -testApproveWithStandardERC20() (gas: 26339) -testTransferFromWithReturnsTooMuch() (gas: 58018) -testFailTransferFromWithReturnsFalse() (gas: 8950) -testApproveWithNonContract() (gas: 3014) -testApproveWithMissingReturn() (gas: 26274) -testFailTransferWithReturnsTooLittle() (gas: 3973) -testApproveWithReturnsTooMuch() (gas: 26655) -testTransferFromWithMissingReturn() (gas: 57195) -testTransferWithStandardERC20() (gas: 27422) -testFailTransferFromWithReturnsTooLittle() (gas: 8788) -testTransferFromWithStandardERC20() (gas: 57282) -testTransferFromWithNonContract() (gas: 3036) -testFailApproveWithReturnsTooLittle() (gas: 1069) -testTransferWithMissingReturn() (gas: 27368) -testFailApproveWithReturnsFalse() (gas: 1126) -testTransferETH() (gas: 34637) -testTransferWithNonContract() (gas: 2990) -testTransferWithReturnsTooMuch() (gas: 27793) -testFailTransferETHToContractWithoutFallback() (gas: 7244) -testFailApproveWithReverting() (gas: 1024) -testFailTransferWithReverting() (gas: 4016) -testPartialWithdraw() (gas: 68781) -testDeposit() (gas: 58760) -testFallbackDeposit() (gas: 59024) -testWithdraw() (gas: 68715) diff --git a/contracts/lib/solmate/.github/pull_request_template.md b/contracts/lib/solmate/.github/pull_request_template.md deleted file mode 100644 index 4f03af8b6f..0000000000 --- a/contracts/lib/solmate/.github/pull_request_template.md +++ /dev/null @@ -1,14 +0,0 @@ -## Description - -Describe the changes made in your pull request here. - -## Checklist - -Ensure you completed **all of the steps** below before submitting your pull request: - -- [ ] Ran `dapp snapshot`? -- [ ] Ran `npm run lint`? -- [ ] Ran `forge test`? -- [ ] Ran `dapp test`? - -_Pull requests with an incomplete checklist will be thrown out._ diff --git a/contracts/lib/solmate/.github/workflows/dapp-tests.yml b/contracts/lib/solmate/.github/workflows/dapp-tests.yml deleted file mode 100644 index e3a8df5c32..0000000000 --- a/contracts/lib/solmate/.github/workflows/dapp-tests.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Dapp Tests - -on: [push, pull_request] - -jobs: - dapp-tests: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 - - uses: cachix/install-nix-action@v13 - - uses: cachix/cachix-action@v10 - with: - name: dapp - - - name: Install dependencies - run: nix-shell --run 'make' - - - name: Check gas snapshots - run: nix-shell --run 'dapp check-snapshot' - - - name: Run tests - run: nix-shell --run 'dapp test' - env: - # Only fuzz deeply if we're pushing to main or this is a PR to main: - DEEP_FUZZ: ${{ github.ref == 'refs/heads/main' || github.base_ref == 'main' }} diff --git a/contracts/lib/solmate/.github/workflows/forge-tests.yml b/contracts/lib/solmate/.github/workflows/forge-tests.yml deleted file mode 100644 index 6c5ef5a23d..0000000000 --- a/contracts/lib/solmate/.github/workflows/forge-tests.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Forge Tests - -on: [push, pull_request] - -jobs: - forge-tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - name: Install Foundry - uses: onbjerg/foundry-toolchain@v1 - with: - version: nightly - - - name: Install dependencies - run: forge install - - - name: Run forge tests - run: forge test diff --git a/contracts/lib/solmate/.gitmodules b/contracts/lib/solmate/.gitmodules deleted file mode 100644 index e12471968b..0000000000 --- a/contracts/lib/solmate/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "lib/ds-test"] - path = lib/ds-test - url = https://github.com/dapphub/ds-test diff --git a/contracts/lib/solmate/.vscode/settings.json b/contracts/lib/solmate/.vscode/settings.json deleted file mode 100644 index c06f7e059a..0000000000 --- a/contracts/lib/solmate/.vscode/settings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "solidity.packageDefaultDependenciesContractsDirectory": "src", - "solidity.packageDefaultDependenciesDirectory": "lib", - "solidity.compileUsingRemoteVersion": "v0.8.10", - "search.exclude": { "lib": true }, - "files.associations": { - ".dapprc": "shellscript", - ".gas-snapshot": "julia" - } -} From cbb1a70592fcb299078d83f838332e7ede1206a8 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Fri, 25 Mar 2022 14:35:42 -0400 Subject: [PATCH 03/13] Exclude solmate from slither. --- contracts/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/package.json b/contracts/package.json index 4232a9bf01..b1f0bf468d 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -22,7 +22,7 @@ "echidna": "yarn run clean && echidna-test . --contract PropertiesOUSDTransferable --config contracts/crytic/TestOUSDTransferable.yaml", "compute-merkle-proofs-local": "HARDHAT_NETWORK=localhost node scripts/staking/airDrop.js reimbursements.csv scripts/staking/merkleProofedAccountsToBeCompensated.json && cp scripts/staking/merkleProofedAccountsToBeCompensated.json ../dapp/src/constants/merkleProofedAccountsToBeCompensated.json", "compute-merkle-proofs-mainnet": "HARDHAT_NETWORK=mainnet node scripts/staking/airDrop.js reimbursements.csv scripts/staking/merkleProofedAccountsToBeCompensated.json && cp scripts/staking/merkleProofedAccountsToBeCompensated.json ../dapp/src/constants/merkleProofedAccountsToBeCompensated.json", - "slither": "yarn run clean && slither . --filter-paths \"crytic|mocks|@openzeppelin\" --exclude-low --exclude-informational --exclude conformance-to-solidity-naming-conventions,different-pragma-directives-are-used,external-function,assembly,incorrect-equality", + "slither": "yarn run clean && slither . --filter-paths \"crytic|mocks|solmate|@openzeppelin\" --exclude-low --exclude-informational --exclude conformance-to-solidity-naming-conventions,different-pragma-directives-are-used,external-function,assembly,incorrect-equality", "clean": "rm -rf build crytic-export artifacts cache deployments/local*", "coverage": "IS_TEST=true npx hardhat coverage" }, From cc9bc31103235cc5ea2362b03a94918a63e4ddf9 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Fri, 25 Mar 2022 14:36:37 -0400 Subject: [PATCH 04/13] Prettier --- contracts/deploy/039_wrapped_ousd.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/deploy/039_wrapped_ousd.js b/contracts/deploy/039_wrapped_ousd.js index 42ab653bf5..7bfafc1158 100644 --- a/contracts/deploy/039_wrapped_ousd.js +++ b/contracts/deploy/039_wrapped_ousd.js @@ -41,7 +41,7 @@ module.exports = deploymentWithProposal( contract: cWousd, signature: "claimGovernance()", }, - ] - } + ], + }; } ); From ab95113b76e4accf49744d10f2414905a4bfcd1b Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Fri, 25 Mar 2022 14:37:44 -0400 Subject: [PATCH 05/13] Disable force deploy --- contracts/deploy/039_wrapped_ousd.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/deploy/039_wrapped_ousd.js b/contracts/deploy/039_wrapped_ousd.js index 7bfafc1158..369378b049 100644 --- a/contracts/deploy/039_wrapped_ousd.js +++ b/contracts/deploy/039_wrapped_ousd.js @@ -4,7 +4,7 @@ const { deploymentWithProposal, withConfirmation } = require("../utils/deploy"); // Upgrade to using it module.exports = deploymentWithProposal( - { deployName: "039_wrapped_ousd", forceDeploy: true }, + { deployName: "039_wrapped_ousd", forceDeploy: false }, async ({ deployWithConfirmation, getTxOpts, ethers }) => { const { deployerAddr, governorAddr } = await getNamedAccounts(); const sDeployer = await ethers.provider.getSigner(deployerAddr); From 7def1231d5b16dc36e8fb7a0dc97e9201db0a712 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Mon, 28 Mar 2022 11:28:22 -0400 Subject: [PATCH 06/13] Use a proxy for wrapped OUSD --- brownie/abi/forcefund.json | 1 + brownie/abi/wrapped_ousd.json | 710 ++++++++++++++++++++++ contracts/contracts/proxies/Proxies.sol | 7 + contracts/contracts/token/WrappedOusd.sol | 16 +- contracts/deploy/001_core.js | 1 + contracts/deploy/039_wrapped_ousd.js | 34 +- contracts/test/token/wousd.js | 12 +- 7 files changed, 770 insertions(+), 11 deletions(-) create mode 100644 brownie/abi/forcefund.json create mode 100644 brownie/abi/wrapped_ousd.json diff --git a/brownie/abi/forcefund.json b/brownie/abi/forcefund.json new file mode 100644 index 0000000000..4b2d56137f --- /dev/null +++ b/brownie/abi/forcefund.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"fund","outputs":[],"stateMutability":"payable","type":"function"}] diff --git a/brownie/abi/wrapped_ousd.json b/brownie/abi/wrapped_ousd.json new file mode 100644 index 0000000000..d84add93cd --- /dev/null +++ b/brownie/abi/wrapped_ousd.json @@ -0,0 +1,710 @@ +[ + { + "inputs": [ + { + "internalType": "contract ERC20", + "name": "_underlying", + "type": "address" + }, + { + "internalType": "string", + "name": "_name", + "type": "string" + }, + { + "internalType": "string", + "name": "_symbol", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "assets", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "assets", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "Withdraw", + "type": "event" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "asset", + "outputs": [ + { + "internalType": "contract ERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "convertToAssets", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + } + ], + "name": "convertToShares", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "deposit", + "outputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "maxDeposit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "maxMint", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "maxRedeem", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "maxWithdraw", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "mint", + "outputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "nonces", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + } + ], + "name": "previewDeposit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "previewMint", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "previewRedeem", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + } + ], + "name": "previewWithdraw", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "redeem", + "outputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalAssets", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "assets", + "type": "uint256" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "withdraw", + "outputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/contracts/contracts/proxies/Proxies.sol b/contracts/contracts/proxies/Proxies.sol index 93da54ec59..5322d39550 100644 --- a/contracts/contracts/proxies/Proxies.sol +++ b/contracts/contracts/proxies/Proxies.sol @@ -10,6 +10,13 @@ contract OUSDProxy is InitializeGovernedUpgradeabilityProxy { } +/** + * @notice WrappedOUSDProxy delegates calls to a WrappedOUSD implementation + */ +contract WrappedOUSDProxy is InitializeGovernedUpgradeabilityProxy { + +} + /** * @notice VaultProxy delegates calls to a Vault implementation */ diff --git a/contracts/contracts/token/WrappedOusd.sol b/contracts/contracts/token/WrappedOusd.sol index c6152c40d5..c88f3b13e3 100644 --- a/contracts/contracts/token/WrappedOusd.sol +++ b/contracts/contracts/token/WrappedOusd.sol @@ -5,19 +5,25 @@ import { ERC20 } from "../../lib/solmate/src/tokens/ERC20.sol"; import { ERC4626 } from "../../lib/solmate/src/mixins/ERC4626.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import { OUSD } from "./OUSD.sol"; + import { Governable } from "../governance/Governable.sol"; +import { Initializable } from "../utils/Initializable.sol"; +import { OUSD } from "./OUSD.sol"; -contract WrappedOusd is ERC4626, Governable { +contract WrappedOusd is ERC4626, Governable, Initializable { using SafeERC20 for IERC20; constructor( ERC20 _underlying, string memory _name, string memory _symbol - ) ERC4626(_underlying, _name, _symbol) Governable() { - OUSD(address(_underlying)).rebaseOptOut(); // It's not treated as a contract yet - OUSD(address(_underlying)).rebaseOptIn(); + ) ERC4626(_underlying, _name, _symbol) Governable() {} + + /** + * @notice Enable OUSD rebasing for this contract + */ + function initialize() external onlyGovernor initializer { + OUSD(address(asset)).rebaseOptIn(); } /** diff --git a/contracts/deploy/001_core.js b/contracts/deploy/001_core.js index 48afb4eeb6..aaf9e5c2b8 100644 --- a/contracts/deploy/001_core.js +++ b/contracts/deploy/001_core.js @@ -717,6 +717,7 @@ const deployWOusd = async () => { "WOUSD", ]); const wousd = await ethers.getContract("WrappedOusd"); + await wousd.connect(sDeployer)["initialize()"](); await wousd.connect(sDeployer).transferGovernance(governorAddr); await wousd.connect(sGovernor).claimGovernance(); }; diff --git a/contracts/deploy/039_wrapped_ousd.js b/contracts/deploy/039_wrapped_ousd.js index 369378b049..22121b9f0a 100644 --- a/contracts/deploy/039_wrapped_ousd.js +++ b/contracts/deploy/039_wrapped_ousd.js @@ -16,16 +16,40 @@ module.exports = deploymentWithProposal( // ---------------- // 1. Deploy the new implementation. - const dWrappedOusd = await deployWithConfirmation("WrappedOusd", [ + const dWrappedOusdImpl = await deployWithConfirmation("WrappedOusd", [ cOUSDProxy.address, "Wrapped OUSD", "WOUSD", ]); - const cWousd = await ethers.getContract("WrappedOusd"); - // 2. Assign ownership + // 2. Deploy the new proxy + const dWrappedOUSDProxy = await deployWithConfirmation("WrappedOUSDProxy"); + const cWrappedOUSDProxy = await ethers.getContract("WrappedOUSDProxy"); + const cWrappedOUSD = await ethers.getContractAt( + "WrappedOusd", + cWrappedOUSDProxy.address + ); + + // 3. Configure Proxy + await withConfirmation( + cWrappedOUSDProxy + .connect(sDeployer) + ["initialize(address,address,bytes)"]( + dWrappedOusdImpl.address, + deployerAddr, + [], + await getTxOpts() + ) + ); + + // 3. Initialize Wrapped OUSD + await withConfirmation( + cWrappedOUSD.connect(sDeployer)["initialize()"](await getTxOpts()) + ); + + // 4. Assign ownership await withConfirmation( - cWousd + cWrappedOUSD .connect(sDeployer) .transferGovernance(governorAddr, await getTxOpts()) ); @@ -38,7 +62,7 @@ module.exports = deploymentWithProposal( actions: [ // 1. Claim governance { - contract: cWousd, + contract: cWrappedOUSD, signature: "claimGovernance()", }, ], diff --git a/contracts/test/token/wousd.js b/contracts/test/token/wousd.js index 0242218fed..8d6dbf7579 100644 --- a/contracts/test/token/wousd.js +++ b/contracts/test/token/wousd.js @@ -8,12 +8,13 @@ describe("WOUSD", function () { this.timeout(0); } - let ousd, wousd, dai, matt, josh, governor; + let ousd, wousd, vault, dai, matt, josh, governor; beforeEach(async () => { const fixture = await loadFixture(defaultFixture); ousd = fixture.ousd; wousd = fixture.wousd; + vault = fixture.vault; dai = fixture.dai; matt = fixture.matt; josh = fixture.josh; @@ -55,6 +56,15 @@ describe("WOUSD", function () { }); }); + describe("Collects Rebase", async () => { + it("should increase with an OUSD rebase", async () => { + await expect(wousd).to.have.approxBalanceOf("100", ousd); + await dai.connect(josh).transfer(vault.address, daiUnits("100")); + await vault.rebase(); + await expect(wousd).to.have.approxBalanceOf("150", ousd); + }); + }); + describe("Token recovery", async () => { it("should allow a governor to recover tokens", async () => { await dai.connect(matt).transfer(wousd.address, daiUnits("2")); From 21d5006773635f859d37b519eef67fc0b039d95c Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Mon, 28 Mar 2022 16:08:46 -0400 Subject: [PATCH 07/13] Update proxy initialize to set non-immutable constructor fields. --- contracts/contracts/token/WrappedOusd.sol | 8 +++++++- contracts/deploy/001_core.js | 22 +++++++++++++++++----- contracts/deploy/039_wrapped_ousd.js | 8 +++++++- contracts/test/_fixture.js | 3 ++- contracts/test/token/wousd.js | 8 ++++++++ 5 files changed, 41 insertions(+), 8 deletions(-) diff --git a/contracts/contracts/token/WrappedOusd.sol b/contracts/contracts/token/WrappedOusd.sol index c88f3b13e3..ea14bbf0ce 100644 --- a/contracts/contracts/token/WrappedOusd.sol +++ b/contracts/contracts/token/WrappedOusd.sol @@ -22,8 +22,14 @@ contract WrappedOusd is ERC4626, Governable, Initializable { /** * @notice Enable OUSD rebasing for this contract */ - function initialize() external onlyGovernor initializer { + function initialize(string memory _name, string memory _symbol) + external + onlyGovernor + initializer + { OUSD(address(asset)).rebaseOptIn(); + name = _name; + symbol = _symbol; } /** diff --git a/contracts/deploy/001_core.js b/contracts/deploy/001_core.js index aaf9e5c2b8..fc17665296 100644 --- a/contracts/deploy/001_core.js +++ b/contracts/deploy/001_core.js @@ -711,13 +711,25 @@ const deployWOusd = async () => { const sDeployer = await ethers.provider.getSigner(deployerAddr); const sGovernor = await ethers.provider.getSigner(governorAddr); const ousd = await ethers.getContract("OUSDProxy"); - await deployWithConfirmation("WrappedOusd", [ + const dWrappedOusdImpl = await deployWithConfirmation("WrappedOusd", [ ousd.address, - "Wrapped OUSD", - "WOUSD", + "Wrapped OUSD IMPL", + "WOUSD IMPL", ]); - const wousd = await ethers.getContract("WrappedOusd"); - await wousd.connect(sDeployer)["initialize()"](); + const dWrappedOusdProxy = await deployWithConfirmation("WrappedOUSDProxy"); + const wousdProxy = await ethers.getContract("WrappedOUSDProxy"); + const wousd = await ethers.getContractAt("WrappedOusd", wousdProxy.address); + + await wousdProxy + .connect(sDeployer) + ["initialize(address,address,bytes)"]( + dWrappedOusdImpl.address, + deployerAddr, + [] + ); + await wousd + .connect(sDeployer) + ["initialize(string,string)"]("Wrapped OUSD", "WOUSD"); await wousd.connect(sDeployer).transferGovernance(governorAddr); await wousd.connect(sGovernor).claimGovernance(); }; diff --git a/contracts/deploy/039_wrapped_ousd.js b/contracts/deploy/039_wrapped_ousd.js index 22121b9f0a..ada31beefa 100644 --- a/contracts/deploy/039_wrapped_ousd.js +++ b/contracts/deploy/039_wrapped_ousd.js @@ -44,7 +44,13 @@ module.exports = deploymentWithProposal( // 3. Initialize Wrapped OUSD await withConfirmation( - cWrappedOUSD.connect(sDeployer)["initialize()"](await getTxOpts()) + cWrappedOUSD + .connect(sDeployer) + ["initialize(string,string)"]( + "Wrapped OUSD", + "WOUSD", + await getTxOpts() + ) ); // 4. Assign ownership diff --git a/contracts/test/_fixture.js b/contracts/test/_fixture.js index 818a02fbc2..9dfa0d1f6b 100644 --- a/contracts/test/_fixture.js +++ b/contracts/test/_fixture.js @@ -38,7 +38,8 @@ async function defaultFixture() { ); const dripperProxy = await ethers.getContract("DripperProxy"); const dripper = await ethers.getContractAt("Dripper", dripperProxy.address); - const wousd = await ethers.getContract("WrappedOusd"); + const wousdProxy = await ethers.getContract("WrappedOUSDProxy"); + const wousd = await ethers.getContractAt("WrappedOusd", wousdProxy.address); const governorContract = await ethers.getContract("Governor"); const CompoundStrategyFactory = await ethers.getContractFactory( "CompoundStrategy" diff --git a/contracts/test/token/wousd.js b/contracts/test/token/wousd.js index 8d6dbf7579..849d7135fd 100644 --- a/contracts/test/token/wousd.js +++ b/contracts/test/token/wousd.js @@ -65,6 +65,14 @@ describe("WOUSD", function () { }); }); + describe("Check proxy", async () => { + it("should have correct ERC20 properties", async () => { + expect(await wousd.decimals()).to.eq(18); + expect(await wousd.name()).to.eq("Wrapped OUSD"); + expect(await wousd.symbol()).to.eq("WOUSD"); + }); + }); + describe("Token recovery", async () => { it("should allow a governor to recover tokens", async () => { await dai.connect(matt).transfer(wousd.address, daiUnits("2")); From 8692371aa7046c12c6d444bc6009cadc58891a2a Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Wed, 30 Mar 2022 14:43:26 -0400 Subject: [PATCH 08/13] Switch to Open Zeppelin 4626 --- contracts/contracts/token/WrappedOusd.sol | 39 ++- .../token/ERC20/extensions/ERC4626.sol | 189 +++++++++++++ .../lib/openzeppelin/interfaces/IERC4626.sol | 233 ++++++++++++++++ contracts/lib/solmate/.gitattributes | 3 - contracts/lib/solmate/.gitignore | 3 - contracts/lib/solmate/.prettierrc | 14 - contracts/lib/solmate/src/auth/Auth.sol | 64 ----- .../auth/authorities/MultiRolesAuthority.sol | 123 --------- .../src/auth/authorities/RolesAuthority.sol | 108 -------- contracts/lib/solmate/src/mixins/ERC4626.sol | 183 ------------- contracts/lib/solmate/src/tokens/ERC1155.sol | 257 ------------------ contracts/lib/solmate/src/tokens/ERC20.sol | 206 -------------- contracts/lib/solmate/src/tokens/ERC721.sol | 216 --------------- contracts/lib/solmate/src/tokens/WETH.sol | 35 --- .../solmate/src/utils/Bytes32AddressLib.sol | 14 - contracts/lib/solmate/src/utils/CREATE3.sol | 82 ------ .../solmate/src/utils/FixedPointMathLib.sol | 222 --------------- .../lib/solmate/src/utils/ReentrancyGuard.sol | 19 -- contracts/lib/solmate/src/utils/SSTORE2.sol | 99 ------- .../lib/solmate/src/utils/SafeCastLib.sol | 61 ----- .../lib/solmate/src/utils/SafeTransferLib.sol | 126 --------- 21 files changed, 441 insertions(+), 1855 deletions(-) create mode 100644 contracts/lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol create mode 100644 contracts/lib/openzeppelin/interfaces/IERC4626.sol delete mode 100644 contracts/lib/solmate/.gitattributes delete mode 100644 contracts/lib/solmate/.gitignore delete mode 100644 contracts/lib/solmate/.prettierrc delete mode 100644 contracts/lib/solmate/src/auth/Auth.sol delete mode 100644 contracts/lib/solmate/src/auth/authorities/MultiRolesAuthority.sol delete mode 100644 contracts/lib/solmate/src/auth/authorities/RolesAuthority.sol delete mode 100644 contracts/lib/solmate/src/mixins/ERC4626.sol delete mode 100644 contracts/lib/solmate/src/tokens/ERC1155.sol delete mode 100644 contracts/lib/solmate/src/tokens/ERC20.sol delete mode 100644 contracts/lib/solmate/src/tokens/ERC721.sol delete mode 100644 contracts/lib/solmate/src/tokens/WETH.sol delete mode 100644 contracts/lib/solmate/src/utils/Bytes32AddressLib.sol delete mode 100644 contracts/lib/solmate/src/utils/CREATE3.sol delete mode 100644 contracts/lib/solmate/src/utils/FixedPointMathLib.sol delete mode 100644 contracts/lib/solmate/src/utils/ReentrancyGuard.sol delete mode 100644 contracts/lib/solmate/src/utils/SSTORE2.sol delete mode 100644 contracts/lib/solmate/src/utils/SafeCastLib.sol delete mode 100644 contracts/lib/solmate/src/utils/SafeTransferLib.sol diff --git a/contracts/contracts/token/WrappedOusd.sol b/contracts/contracts/token/WrappedOusd.sol index ea14bbf0ce..de34fcb875 100644 --- a/contracts/contracts/token/WrappedOusd.sol +++ b/contracts/contracts/token/WrappedOusd.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { ERC20 } from "../../lib/solmate/src/tokens/ERC20.sol"; -import { ERC4626 } from "../../lib/solmate/src/mixins/ERC4626.sol"; +import { ERC4626 } from "../../lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; @@ -14,42 +14,41 @@ contract WrappedOusd is ERC4626, Governable, Initializable { using SafeERC20 for IERC20; constructor( - ERC20 _underlying, - string memory _name, - string memory _symbol - ) ERC4626(_underlying, _name, _symbol) Governable() {} + ERC20 underlying_, + string memory name_, + string memory symbol_ + ) ERC20(name_, symbol_) ERC4626(underlying_) Governable() {} /** * @notice Enable OUSD rebasing for this contract */ - function initialize(string memory _name, string memory _symbol) + function initialize(string memory name_, string memory symbol_) external onlyGovernor initializer { - OUSD(address(asset)).rebaseOptIn(); - name = _name; - symbol = _symbol; + OUSD(address(asset())).rebaseOptIn(); } - /** - * @notice Show the total amount of OUSD held by the wrapper - */ - function totalAssets() public view override returns (uint256) { - return ERC20(asset).balanceOf(address(this)); + function name() public view override returns (string memory) { + return "Wrapped OUSD"; + } + + function symbol() public view override returns (string memory) { + return "WOUSD"; } /** * @notice Transfer token to governor. Intended for recovering tokens stuck in * contract, i.e. mistaken sends. Cannot transfer OUSD - * @param _asset Address for the asset - * @param _amount Amount of the asset to transfer + * @param asset_ Address for the asset + * @param amount_ Amount of the asset to transfer */ - function transferToken(address _asset, uint256 _amount) + function transferToken(address asset_, uint256 amount_) external onlyGovernor { - require(_asset != address(asset), "Cannot collect OUSD"); - IERC20(_asset).safeTransfer(governor(), _amount); + require(asset_ != address(asset()), "Cannot collect OUSD"); + IERC20(asset_).safeTransfer(governor(), amount_); } } diff --git a/contracts/lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol b/contracts/lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol new file mode 100644 index 0000000000..9a0692fb26 --- /dev/null +++ b/contracts/lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import { IERC4626 } from "../../../../interfaces/IERC4626.sol"; +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; + +abstract contract ERC4626 is ERC20, IERC4626 { + IERC20Metadata private immutable _asset; + + 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} + * + * Will revert if asserts > 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) public view virtual override returns (uint256 shares) { + uint256 supply = totalSupply(); + + return + (assets == 0 || supply == 0) + ? (assets * 10**decimals()) / 10**_asset.decimals() + : (assets * supply) / totalAssets(); + } + + /** @dev See {IERC4262-convertToAssets} */ + function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) { + uint256 supply = totalSupply(); + + return (supply == 0) ? (shares * 10**_asset.decimals()) / 10**decimals() : (shares * totalAssets()) / supply; + } + + /** @dev See {IERC4262-maxDeposit} */ + function maxDeposit(address) public view virtual override returns (uint256) { + return type(uint256).max; + } + + /** @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)); + } + + /** @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); + } + + /** @dev See {IERC4262-previewMint} */ + function previewMint(uint256 shares) public view virtual override returns (uint256) { + uint256 assets = convertToAssets(shares); + return assets + (convertToShares(assets) < shares ? 1 : 0); + } + + /** @dev See {IERC4262-previewWithdraw} */ + function previewWithdraw(uint256 assets) public view virtual override returns (uint256) { + uint256 shares = convertToShares(assets); + return shares + (convertToAssets(shares) < assets ? 1 : 0); + } + + /** @dev See {IERC4262-previewRedeem} */ + function previewRedeem(uint256 shares) public view virtual override returns (uint256) { + return convertToAssets(shares); + } + + /** @dev See {IERC4262-deposit} */ + function deposit(uint256 assets, address receiver) public virtual override returns (uint256) { + require(assets <= maxDeposit(receiver), "ERC4626: deposit more then max"); + + address caller = _msgSender(); + uint256 shares = previewDeposit(assets); + + // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through + // the tokensToSend hook, so we need to transfer before we mint to keep the invariants. + SafeERC20.safeTransferFrom(_asset, caller, address(this), assets); + _mint(receiver, shares); + + emit Deposit(caller, receiver, assets, shares); + + return shares; + } + + /** @dev See {IERC4262-mint} */ + function mint(uint256 shares, address receiver) public virtual override returns (uint256) { + require(shares <= maxMint(receiver), "ERC4626: mint more then max"); + + address caller = _msgSender(); + uint256 assets = previewMint(shares); + + // if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through + // the tokensToSend hook, so we need to transfer before we mint to keep the invariants. + SafeERC20.safeTransferFrom(_asset, caller, address(this), assets); + _mint(receiver, shares); + + emit Deposit(caller, 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), "ERC4626: withdraw more then max"); + + address caller = _msgSender(); + uint256 shares = previewWithdraw(assets); + + if (caller != owner) { + _spendAllowance(owner, caller, shares); + } + + // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through + // the tokensReceived hook, so we need to transfer after we burn to keep the invariants. + _burn(owner, shares); + SafeERC20.safeTransfer(_asset, receiver, assets); + + emit Withdraw(caller, 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), "ERC4626: redeem more then max"); + + address caller = _msgSender(); + uint256 assets = previewRedeem(shares); + + if (caller != owner) { + _spendAllowance(owner, caller, shares); + } + + // if _asset is ERC777, transfer can call reenter AFTER the transfer happens through + // the tokensReceived hook, so we need to transfer after we burn to keep the invariants. + _burn(owner, shares); + SafeERC20.safeTransfer(_asset, receiver, assets); + + emit Withdraw(caller, receiver, owner, assets, shares); + + return assets; + } + + + function _spendAllowance( + address owner, + address spender, + uint256 amount + ) internal virtual { + uint256 currentAllowance = allowance(owner, spender); + if (currentAllowance != type(uint256).max) { + require(currentAllowance >= amount, "ERC20: insufficient allowance"); + unchecked { + _approve(owner, spender, currentAllowance - amount); + } + } + } +} \ No newline at end of file diff --git a/contracts/lib/openzeppelin/interfaces/IERC4626.sol b/contracts/lib/openzeppelin/interfaces/IERC4626.sol new file mode 100644 index 0000000000..506e9e2dcd --- /dev/null +++ b/contracts/lib/openzeppelin/interfaces/IERC4626.sol @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +interface IERC4626 is IERC20, IERC20Metadata { + event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares); + + event Withdraw( + address indexed caller, + address indexed receiver, + address indexed owner, + uint256 assets, + uint256 shares + ); + + /** + * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. + * + * - MUST be an ERC-20 token contract. + * - MUST NOT revert. + */ + function asset() external view returns (address assetTokenAddress); + + /** + * @dev Returns the total amount of the underlying asset that is “managed” by Vault. + * + * - SHOULD include any compounding that occurs from yield. + * - MUST be inclusive of any fees that are charged against assets in the Vault. + * - MUST NOT revert. + */ + function totalAssets() external view returns (uint256 totalManagedAssets); + + /** + * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal + * scenario where all the conditions are met. + * + * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. + * - MUST NOT show any variations depending on the caller. + * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. + * - MUST NOT revert. + * + * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the + * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and + * from. + */ + function convertToShares(uint256 assets) external view returns (uint256 shares); + + /** + * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal + * scenario where all the conditions are met. + * + * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. + * - MUST NOT show any variations depending on the caller. + * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. + * - MUST NOT revert. + * + * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the + * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and + * from. + */ + function convertToAssets(uint256 shares) external view returns (uint256 assets); + + /** + * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver, + * through a deposit call. + * + * - MUST return a limited value if receiver is subject to some deposit limit. + * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. + * - MUST NOT revert. + */ + function maxDeposit(address receiver) external view returns (uint256 maxAssets); + + /** + * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given + * current on-chain conditions. + * + * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit + * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called + * in the same transaction. + * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the + * deposit would be accepted, regardless if the user has enough tokens approved, etc. + * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. + * - MUST NOT revert. + * + * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in + * share price or some other type of condition, meaning the depositor will lose assets by depositing. + */ + function previewDeposit(uint256 assets) external view returns (uint256 shares); + + /** + * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens. + * + * - MUST emit the Deposit event. + * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the + * deposit execution, and are accounted for during deposit. + * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not + * approving enough underlying tokens to the Vault contract, etc). + * + * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. + */ + function deposit(uint256 assets, address receiver) external returns (uint256 shares); + + /** + * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call. + * - MUST return a limited value if receiver is subject to some mint limit. + * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted. + * - MUST NOT revert. + */ + function maxMint(address receiver) external view returns (uint256 maxShares); + + /** + * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given + * current on-chain conditions. + * + * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call + * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the + * same transaction. + * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint + * would be accepted, regardless if the user has enough tokens approved, etc. + * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. + * - MUST NOT revert. + * + * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in + * share price or some other type of condition, meaning the depositor will lose assets by minting. + */ + function previewMint(uint256 shares) external view returns (uint256 assets); + + /** + * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. + * + * - MUST emit the Deposit event. + * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint + * execution, and are accounted for during mint. + * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not + * approving enough underlying tokens to the Vault contract, etc). + * + * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. + */ + function mint(uint256 shares, address receiver) external returns (uint256 assets); + + /** + * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the + * Vault, through a withdraw call. + * + * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. + * - MUST NOT revert. + */ + function maxWithdraw(address owner) external view returns (uint256 maxAssets); + + /** + * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, + * given current on-chain conditions. + * + * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw + * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if + * called + * in the same transaction. + * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though + * the withdrawal would be accepted, regardless if the user has enough shares, etc. + * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. + * - MUST NOT revert. + * + * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in + * share price or some other type of condition, meaning the depositor will lose assets by depositing. + */ + function previewWithdraw(uint256 assets) external view returns (uint256 shares); + + /** + * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver. + * + * - MUST emit the Withdraw event. + * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the + * withdraw execution, and are accounted for during withdraw. + * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner + * not having enough shares, etc). + * + * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. + * Those methods should be performed separately. + */ + function withdraw( + uint256 assets, + address receiver, + address owner + ) external returns (uint256 shares); + + /** + * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, + * through a redeem call. + * + * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. + * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock. + * - MUST NOT revert. + */ + function maxRedeem(address owner) external view returns (uint256 maxShares); + + /** + * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, + * given current on-chain conditions. + * + * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call + * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the + * same transaction. + * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the + * redemption would be accepted, regardless if the user has enough shares, etc. + * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. + * - MUST NOT revert. + * + * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in + * share price or some other type of condition, meaning the depositor will lose assets by redeeming. + */ + function previewRedeem(uint256 shares) external view returns (uint256 assets); + + /** + * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver. + * + * - MUST emit the Withdraw event. + * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the + * redeem execution, and are accounted for during redeem. + * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner + * not having enough shares, etc). + * + * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed. + * Those methods should be performed separately. + */ + function redeem( + uint256 shares, + address receiver, + address owner + ) external returns (uint256 assets); +} \ No newline at end of file diff --git a/contracts/lib/solmate/.gitattributes b/contracts/lib/solmate/.gitattributes deleted file mode 100644 index 745230d431..0000000000 --- a/contracts/lib/solmate/.gitattributes +++ /dev/null @@ -1,3 +0,0 @@ -*.sol linguist-language=Solidity -.dapprc linguist-language=Shell -.gas-snapshot linguist-language=Julia \ No newline at end of file diff --git a/contracts/lib/solmate/.gitignore b/contracts/lib/solmate/.gitignore deleted file mode 100644 index 5dfe93fbde..0000000000 --- a/contracts/lib/solmate/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/cache -/node_modules -/out \ No newline at end of file diff --git a/contracts/lib/solmate/.prettierrc b/contracts/lib/solmate/.prettierrc deleted file mode 100644 index 15ae8a76d4..0000000000 --- a/contracts/lib/solmate/.prettierrc +++ /dev/null @@ -1,14 +0,0 @@ -{ - "tabWidth": 2, - "printWidth": 100, - - "overrides": [ - { - "files": "*.sol", - "options": { - "tabWidth": 4, - "printWidth": 120 - } - } - ] -} diff --git a/contracts/lib/solmate/src/auth/Auth.sol b/contracts/lib/solmate/src/auth/Auth.sol deleted file mode 100644 index 2cf7559217..0000000000 --- a/contracts/lib/solmate/src/auth/Auth.sol +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic. -/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Auth.sol) -/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol) -abstract contract Auth { - event OwnerUpdated(address indexed user, address indexed newOwner); - - event AuthorityUpdated(address indexed user, Authority indexed newAuthority); - - address public owner; - - Authority public authority; - - constructor(address _owner, Authority _authority) { - owner = _owner; - authority = _authority; - - emit OwnerUpdated(msg.sender, _owner); - emit AuthorityUpdated(msg.sender, _authority); - } - - modifier requiresAuth() { - require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED"); - - _; - } - - function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) { - Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas. - - // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be - // aware that this makes protected functions uncallable even to the owner if the authority is out of order. - return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner; - } - - function setAuthority(Authority newAuthority) public virtual { - // We check if the caller is the owner first because we want to ensure they can - // always swap out the authority even if it's reverting or using up a lot of gas. - require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig)); - - authority = newAuthority; - - emit AuthorityUpdated(msg.sender, newAuthority); - } - - function setOwner(address newOwner) public virtual requiresAuth { - owner = newOwner; - - emit OwnerUpdated(msg.sender, newOwner); - } -} - -/// @notice A generic interface for a contract which provides authorization data to an Auth instance. -/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Auth.sol) -/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol) -interface Authority { - function canCall( - address user, - address target, - bytes4 functionSig - ) external view returns (bool); -} diff --git a/contracts/lib/solmate/src/auth/authorities/MultiRolesAuthority.sol b/contracts/lib/solmate/src/auth/authorities/MultiRolesAuthority.sol deleted file mode 100644 index 3329714c9a..0000000000 --- a/contracts/lib/solmate/src/auth/authorities/MultiRolesAuthority.sol +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity 0.8.10; - -import {Auth, Authority} from "../Auth.sol"; - -/// @notice Flexible and target agnostic role based Authority that supports up to 256 roles. -/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/authorities/MultiRolesAuthority.sol) -contract MultiRolesAuthority is Auth, Authority { - /*/////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event UserRoleUpdated(address indexed user, uint8 indexed role, bool enabled); - - event PublicCapabilityUpdated(bytes4 indexed functionSig, bool enabled); - - event RoleCapabilityUpdated(uint8 indexed role, bytes4 indexed functionSig, bool enabled); - - event TargetCustomAuthorityUpdated(address indexed target, Authority indexed authority); - - /*/////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor(address _owner, Authority _authority) Auth(_owner, _authority) {} - - /*/////////////////////////////////////////////////////////////// - CUSTOM TARGET AUTHORITY STORAGE - //////////////////////////////////////////////////////////////*/ - - mapping(address => Authority) public getTargetCustomAuthority; - - /*/////////////////////////////////////////////////////////////// - ROLE/USER STORAGE - //////////////////////////////////////////////////////////////*/ - - mapping(address => bytes32) public getUserRoles; - - mapping(bytes4 => bool) public isCapabilityPublic; - - mapping(bytes4 => bytes32) public getRolesWithCapability; - - function doesUserHaveRole(address user, uint8 role) public view virtual returns (bool) { - return (uint256(getUserRoles[user]) >> role) & 1 != 0; - } - - function doesRoleHaveCapability(uint8 role, bytes4 functionSig) public view virtual returns (bool) { - return (uint256(getRolesWithCapability[functionSig]) >> role) & 1 != 0; - } - - /*/////////////////////////////////////////////////////////////// - AUTHORIZATION LOGIC - //////////////////////////////////////////////////////////////*/ - - function canCall( - address user, - address target, - bytes4 functionSig - ) public view virtual override returns (bool) { - Authority customAuthority = getTargetCustomAuthority[target]; - - if (address(customAuthority) != address(0)) return customAuthority.canCall(user, target, functionSig); - - return - isCapabilityPublic[functionSig] || bytes32(0) != getUserRoles[user] & getRolesWithCapability[functionSig]; - } - - /*/////////////////////////////////////////////////////////////// - CUSTOM TARGET AUTHORITY CONFIGURATION LOGIC - //////////////////////////////////////////////////////////////*/ - - function setTargetCustomAuthority(address target, Authority customAuthority) public virtual requiresAuth { - getTargetCustomAuthority[target] = customAuthority; - - emit TargetCustomAuthorityUpdated(target, customAuthority); - } - - /*/////////////////////////////////////////////////////////////// - PUBLIC CAPABILITY CONFIGURATION LOGIC - //////////////////////////////////////////////////////////////*/ - - function setPublicCapability(bytes4 functionSig, bool enabled) public virtual requiresAuth { - isCapabilityPublic[functionSig] = enabled; - - emit PublicCapabilityUpdated(functionSig, enabled); - } - - /*/////////////////////////////////////////////////////////////// - USER ROLE ASSIGNMENT LOGIC - //////////////////////////////////////////////////////////////*/ - - function setUserRole( - address user, - uint8 role, - bool enabled - ) public virtual requiresAuth { - if (enabled) { - getUserRoles[user] |= bytes32(1 << role); - } else { - getUserRoles[user] &= ~bytes32(1 << role); - } - - emit UserRoleUpdated(user, role, enabled); - } - - /*/////////////////////////////////////////////////////////////// - ROLE CAPABILITY CONFIGURATION LOGIC - //////////////////////////////////////////////////////////////*/ - - function setRoleCapability( - uint8 role, - bytes4 functionSig, - bool enabled - ) public virtual requiresAuth { - if (enabled) { - getRolesWithCapability[functionSig] |= bytes32(1 << role); - } else { - getRolesWithCapability[functionSig] &= ~bytes32(1 << role); - } - - emit RoleCapabilityUpdated(role, functionSig, enabled); - } -} diff --git a/contracts/lib/solmate/src/auth/authorities/RolesAuthority.sol b/contracts/lib/solmate/src/auth/authorities/RolesAuthority.sol deleted file mode 100644 index 94e394f6a4..0000000000 --- a/contracts/lib/solmate/src/auth/authorities/RolesAuthority.sol +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -import {Auth, Authority} from "../Auth.sol"; - -/// @notice Role based Authority that supports up to 256 roles. -/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/authorities/RolesAuthority.sol) -/// @author Modified from Dappsys (https://github.com/dapphub/ds-roles/blob/master/src/roles.sol) -contract RolesAuthority is Auth, Authority { - /*/////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event UserRoleUpdated(address indexed user, uint8 indexed role, bool enabled); - - event PublicCapabilityUpdated(address indexed target, bytes4 indexed functionSig, bool enabled); - - event RoleCapabilityUpdated(uint8 indexed role, address indexed target, bytes4 indexed functionSig, bool enabled); - - /*/////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor(address _owner, Authority _authority) Auth(_owner, _authority) {} - - /*/////////////////////////////////////////////////////////////// - ROLE/USER STORAGE - //////////////////////////////////////////////////////////////*/ - - mapping(address => bytes32) public getUserRoles; - - mapping(address => mapping(bytes4 => bool)) public isCapabilityPublic; - - mapping(address => mapping(bytes4 => bytes32)) public getRolesWithCapability; - - function doesUserHaveRole(address user, uint8 role) public view virtual returns (bool) { - return (uint256(getUserRoles[user]) >> role) & 1 != 0; - } - - function doesRoleHaveCapability( - uint8 role, - address target, - bytes4 functionSig - ) public view virtual returns (bool) { - return (uint256(getRolesWithCapability[target][functionSig]) >> role) & 1 != 0; - } - - /*/////////////////////////////////////////////////////////////// - AUTHORIZATION LOGIC - //////////////////////////////////////////////////////////////*/ - - function canCall( - address user, - address target, - bytes4 functionSig - ) public view virtual override returns (bool) { - return - isCapabilityPublic[target][functionSig] || - bytes32(0) != getUserRoles[user] & getRolesWithCapability[target][functionSig]; - } - - /*/////////////////////////////////////////////////////////////// - ROLE CAPABILITY CONFIGURATION LOGIC - //////////////////////////////////////////////////////////////*/ - - function setPublicCapability( - address target, - bytes4 functionSig, - bool enabled - ) public virtual requiresAuth { - isCapabilityPublic[target][functionSig] = enabled; - - emit PublicCapabilityUpdated(target, functionSig, enabled); - } - - function setRoleCapability( - uint8 role, - address target, - bytes4 functionSig, - bool enabled - ) public virtual requiresAuth { - if (enabled) { - getRolesWithCapability[target][functionSig] |= bytes32(1 << role); - } else { - getRolesWithCapability[target][functionSig] &= ~bytes32(1 << role); - } - - emit RoleCapabilityUpdated(role, target, functionSig, enabled); - } - - /*/////////////////////////////////////////////////////////////// - USER ROLE ASSIGNMENT LOGIC - //////////////////////////////////////////////////////////////*/ - - function setUserRole( - address user, - uint8 role, - bool enabled - ) public virtual requiresAuth { - if (enabled) { - getUserRoles[user] |= bytes32(1 << role); - } else { - getUserRoles[user] &= ~bytes32(1 << role); - } - - emit UserRoleUpdated(user, role, enabled); - } -} diff --git a/contracts/lib/solmate/src/mixins/ERC4626.sol b/contracts/lib/solmate/src/mixins/ERC4626.sol deleted file mode 100644 index 7afc7796c1..0000000000 --- a/contracts/lib/solmate/src/mixins/ERC4626.sol +++ /dev/null @@ -1,183 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -import {ERC20} from "../tokens/ERC20.sol"; -import {SafeTransferLib} from "../utils/SafeTransferLib.sol"; -import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol"; - -/// @notice Minimal ERC4626 tokenized Vault implementation. -/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/mixins/ERC4626.sol) -abstract contract ERC4626 is ERC20 { - using SafeTransferLib for ERC20; - using FixedPointMathLib for uint256; - - /*/////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares); - - event Withdraw( - address indexed caller, - address indexed receiver, - address indexed owner, - uint256 assets, - uint256 shares - ); - - /*/////////////////////////////////////////////////////////////// - IMMUTABLES - //////////////////////////////////////////////////////////////*/ - - ERC20 public immutable asset; - - constructor( - ERC20 _asset, - string memory _name, - string memory _symbol - ) ERC20(_name, _symbol, _asset.decimals()) { - asset = _asset; - } - - /*/////////////////////////////////////////////////////////////// - DEPOSIT/WITHDRAWAL LOGIC - //////////////////////////////////////////////////////////////*/ - - function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) { - // Check for rounding error since we round down in previewDeposit. - require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES"); - - // Need to transfer before minting or ERC777s could reenter. - asset.safeTransferFrom(msg.sender, address(this), assets); - - _mint(receiver, shares); - - emit Deposit(msg.sender, receiver, assets, shares); - - afterDeposit(assets, shares); - } - - function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) { - assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up. - - // Need to transfer before minting or ERC777s could reenter. - asset.safeTransferFrom(msg.sender, address(this), assets); - - _mint(receiver, shares); - - emit Deposit(msg.sender, receiver, assets, shares); - - afterDeposit(assets, shares); - } - - function withdraw( - uint256 assets, - address receiver, - address owner - ) public virtual returns (uint256 shares) { - shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up. - - if (msg.sender != owner) { - uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. - - if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; - } - - beforeWithdraw(assets, shares); - - _burn(owner, shares); - - emit Withdraw(msg.sender, receiver, owner, assets, shares); - - asset.safeTransfer(receiver, assets); - } - - function redeem( - uint256 shares, - address receiver, - address owner - ) public virtual returns (uint256 assets) { - if (msg.sender != owner) { - uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. - - if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; - } - - // Check for rounding error since we round down in previewRedeem. - require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS"); - - beforeWithdraw(assets, shares); - - _burn(owner, shares); - - emit Withdraw(msg.sender, receiver, owner, assets, shares); - - asset.safeTransfer(receiver, assets); - } - - /*/////////////////////////////////////////////////////////////// - ACCOUNTING LOGIC - //////////////////////////////////////////////////////////////*/ - - function totalAssets() public view virtual returns (uint256); - - function convertToShares(uint256 assets) public view returns (uint256) { - uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. - - return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets()); - } - - function convertToAssets(uint256 shares) public view returns (uint256) { - uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. - - return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply); - } - - function previewDeposit(uint256 assets) public view virtual returns (uint256) { - return convertToShares(assets); - } - - function previewMint(uint256 shares) public view virtual returns (uint256) { - uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. - - return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply); - } - - function previewWithdraw(uint256 assets) public view virtual returns (uint256) { - uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. - - return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets()); - } - - function previewRedeem(uint256 shares) public view virtual returns (uint256) { - return convertToAssets(shares); - } - - /*/////////////////////////////////////////////////////////////// - DEPOSIT/WITHDRAWAL LIMIT LOGIC - //////////////////////////////////////////////////////////////*/ - - function maxDeposit(address) public view virtual returns (uint256) { - return type(uint256).max; - } - - function maxMint(address) public view virtual returns (uint256) { - return type(uint256).max; - } - - function maxWithdraw(address owner) public view virtual returns (uint256) { - return convertToAssets(balanceOf[owner]); - } - - function maxRedeem(address owner) public view virtual returns (uint256) { - return balanceOf[owner]; - } - - /*/////////////////////////////////////////////////////////////// - INTERNAL HOOKS LOGIC - //////////////////////////////////////////////////////////////*/ - - function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {} - - function afterDeposit(uint256 assets, uint256 shares) internal virtual {} -} diff --git a/contracts/lib/solmate/src/tokens/ERC1155.sol b/contracts/lib/solmate/src/tokens/ERC1155.sol deleted file mode 100644 index 7ec9eb2f14..0000000000 --- a/contracts/lib/solmate/src/tokens/ERC1155.sol +++ /dev/null @@ -1,257 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Minimalist and gas efficient standard ERC1155 implementation. -/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol) -abstract contract ERC1155 { - /*/////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event TransferSingle( - address indexed operator, - address indexed from, - address indexed to, - uint256 id, - uint256 amount - ); - - event TransferBatch( - address indexed operator, - address indexed from, - address indexed to, - uint256[] ids, - uint256[] amounts - ); - - event ApprovalForAll(address indexed owner, address indexed operator, bool approved); - - event URI(string value, uint256 indexed id); - - /*/////////////////////////////////////////////////////////////// - ERC1155 STORAGE - //////////////////////////////////////////////////////////////*/ - - mapping(address => mapping(uint256 => uint256)) public balanceOf; - - mapping(address => mapping(address => bool)) public isApprovedForAll; - - /*/////////////////////////////////////////////////////////////// - METADATA LOGIC - //////////////////////////////////////////////////////////////*/ - - function uri(uint256 id) public view virtual returns (string memory); - - /*/////////////////////////////////////////////////////////////// - ERC1155 LOGIC - //////////////////////////////////////////////////////////////*/ - - function setApprovalForAll(address operator, bool approved) public virtual { - isApprovedForAll[msg.sender][operator] = approved; - - emit ApprovalForAll(msg.sender, operator, approved); - } - - function safeTransferFrom( - address from, - address to, - uint256 id, - uint256 amount, - bytes memory data - ) public virtual { - require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED"); - - balanceOf[from][id] -= amount; - balanceOf[to][id] += amount; - - emit TransferSingle(msg.sender, from, to, id, amount); - - require( - to.code.length == 0 - ? to != address(0) - : ERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) == - ERC1155TokenReceiver.onERC1155Received.selector, - "UNSAFE_RECIPIENT" - ); - } - - function safeBatchTransferFrom( - address from, - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) public virtual { - uint256 idsLength = ids.length; // Saves MLOADs. - - require(idsLength == amounts.length, "LENGTH_MISMATCH"); - - require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED"); - - // Storing these outside the loop saves ~15 gas per iteration. - uint256 id; - uint256 amount; - - for (uint256 i = 0; i < idsLength; ) { - id = ids[i]; - amount = amounts[i]; - - balanceOf[from][id] -= amount; - balanceOf[to][id] += amount; - - // An array can't have a total length - // larger than the max uint256 value. - unchecked { - ++i; - } - } - - emit TransferBatch(msg.sender, from, to, ids, amounts); - - require( - to.code.length == 0 - ? to != address(0) - : ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) == - ERC1155TokenReceiver.onERC1155BatchReceived.selector, - "UNSAFE_RECIPIENT" - ); - } - - function balanceOfBatch(address[] memory owners, uint256[] memory ids) - public - view - virtual - returns (uint256[] memory balances) - { - uint256 ownersLength = owners.length; // Saves MLOADs. - - require(ownersLength == ids.length, "LENGTH_MISMATCH"); - - balances = new uint256[](ownersLength); - - // Unchecked because the only math done is incrementing - // the array index counter which cannot possibly overflow. - unchecked { - for (uint256 i = 0; i < ownersLength; ++i) { - balances[i] = balanceOf[owners[i]][ids[i]]; - } - } - } - - /*/////////////////////////////////////////////////////////////// - ERC165 LOGIC - //////////////////////////////////////////////////////////////*/ - - function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { - return - interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 - interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155 - interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI - } - - /*/////////////////////////////////////////////////////////////// - INTERNAL MINT/BURN LOGIC - //////////////////////////////////////////////////////////////*/ - - function _mint( - address to, - uint256 id, - uint256 amount, - bytes memory data - ) internal { - balanceOf[to][id] += amount; - - emit TransferSingle(msg.sender, address(0), to, id, amount); - - require( - to.code.length == 0 - ? to != address(0) - : ERC1155TokenReceiver(to).onERC1155Received(msg.sender, address(0), id, amount, data) == - ERC1155TokenReceiver.onERC1155Received.selector, - "UNSAFE_RECIPIENT" - ); - } - - function _batchMint( - address to, - uint256[] memory ids, - uint256[] memory amounts, - bytes memory data - ) internal { - uint256 idsLength = ids.length; // Saves MLOADs. - - require(idsLength == amounts.length, "LENGTH_MISMATCH"); - - for (uint256 i = 0; i < idsLength; ) { - balanceOf[to][ids[i]] += amounts[i]; - - // An array can't have a total length - // larger than the max uint256 value. - unchecked { - ++i; - } - } - - emit TransferBatch(msg.sender, address(0), to, ids, amounts); - - require( - to.code.length == 0 - ? to != address(0) - : ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, address(0), ids, amounts, data) == - ERC1155TokenReceiver.onERC1155BatchReceived.selector, - "UNSAFE_RECIPIENT" - ); - } - - function _batchBurn( - address from, - uint256[] memory ids, - uint256[] memory amounts - ) internal { - uint256 idsLength = ids.length; // Saves MLOADs. - - require(idsLength == amounts.length, "LENGTH_MISMATCH"); - - for (uint256 i = 0; i < idsLength; ) { - balanceOf[from][ids[i]] -= amounts[i]; - - // An array can't have a total length - // larger than the max uint256 value. - unchecked { - ++i; - } - } - - emit TransferBatch(msg.sender, from, address(0), ids, amounts); - } - - function _burn( - address from, - uint256 id, - uint256 amount - ) internal { - balanceOf[from][id] -= amount; - - emit TransferSingle(msg.sender, from, address(0), id, amount); - } -} - -/// @notice A generic interface for a contract which properly accepts ERC1155 tokens. -/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol) -interface ERC1155TokenReceiver { - function onERC1155Received( - address operator, - address from, - uint256 id, - uint256 amount, - bytes calldata data - ) external returns (bytes4); - - function onERC1155BatchReceived( - address operator, - address from, - uint256[] calldata ids, - uint256[] calldata amounts, - bytes calldata data - ) external returns (bytes4); -} diff --git a/contracts/lib/solmate/src/tokens/ERC20.sol b/contracts/lib/solmate/src/tokens/ERC20.sol deleted file mode 100644 index ea89a35d62..0000000000 --- a/contracts/lib/solmate/src/tokens/ERC20.sol +++ /dev/null @@ -1,206 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. -/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol) -/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) -/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. -abstract contract ERC20 { - /*/////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Transfer(address indexed from, address indexed to, uint256 amount); - - event Approval(address indexed owner, address indexed spender, uint256 amount); - - /*/////////////////////////////////////////////////////////////// - METADATA STORAGE - //////////////////////////////////////////////////////////////*/ - - string public name; - - string public symbol; - - uint8 public immutable decimals; - - /*/////////////////////////////////////////////////////////////// - ERC20 STORAGE - //////////////////////////////////////////////////////////////*/ - - uint256 public totalSupply; - - mapping(address => uint256) public balanceOf; - - mapping(address => mapping(address => uint256)) public allowance; - - /*/////////////////////////////////////////////////////////////// - EIP-2612 STORAGE - //////////////////////////////////////////////////////////////*/ - - uint256 internal immutable INITIAL_CHAIN_ID; - - bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; - - mapping(address => uint256) public nonces; - - /*/////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor( - string memory _name, - string memory _symbol, - uint8 _decimals - ) { - name = _name; - symbol = _symbol; - decimals = _decimals; - - INITIAL_CHAIN_ID = block.chainid; - INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); - } - - /*/////////////////////////////////////////////////////////////// - ERC20 LOGIC - //////////////////////////////////////////////////////////////*/ - - function approve(address spender, uint256 amount) public virtual returns (bool) { - allowance[msg.sender][spender] = amount; - - emit Approval(msg.sender, spender, amount); - - return true; - } - - function transfer(address to, uint256 amount) public virtual returns (bool) { - balanceOf[msg.sender] -= amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(msg.sender, to, amount); - - return true; - } - - function transferFrom( - address from, - address to, - uint256 amount - ) public virtual returns (bool) { - uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. - - if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; - - balanceOf[from] -= amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(from, to, amount); - - return true; - } - - /*/////////////////////////////////////////////////////////////// - EIP-2612 LOGIC - //////////////////////////////////////////////////////////////*/ - - function permit( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) public virtual { - require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); - - // Unchecked because the only math done is incrementing - // the owner's nonce which cannot realistically overflow. - unchecked { - address recoveredAddress = ecrecover( - keccak256( - abi.encodePacked( - "\x19\x01", - DOMAIN_SEPARATOR(), - keccak256( - abi.encode( - keccak256( - "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" - ), - owner, - spender, - value, - nonces[owner]++, - deadline - ) - ) - ) - ), - v, - r, - s - ); - - require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); - - allowance[recoveredAddress][spender] = value; - } - - emit Approval(owner, spender, value); - } - - function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { - return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); - } - - function computeDomainSeparator() internal view virtual returns (bytes32) { - return - keccak256( - abi.encode( - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), - keccak256(bytes(name)), - keccak256("1"), - block.chainid, - address(this) - ) - ); - } - - /*/////////////////////////////////////////////////////////////// - INTERNAL MINT/BURN LOGIC - //////////////////////////////////////////////////////////////*/ - - function _mint(address to, uint256 amount) internal virtual { - totalSupply += amount; - - // Cannot overflow because the sum of all user - // balances can't exceed the max uint256 value. - unchecked { - balanceOf[to] += amount; - } - - emit Transfer(address(0), to, amount); - } - - function _burn(address from, uint256 amount) internal virtual { - balanceOf[from] -= amount; - - // Cannot underflow because a user's balance - // will never be larger than the total supply. - unchecked { - totalSupply -= amount; - } - - emit Transfer(from, address(0), amount); - } -} diff --git a/contracts/lib/solmate/src/tokens/ERC721.sol b/contracts/lib/solmate/src/tokens/ERC721.sol deleted file mode 100644 index 1c3d838493..0000000000 --- a/contracts/lib/solmate/src/tokens/ERC721.sol +++ /dev/null @@ -1,216 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Modern, minimalist, and gas efficient ERC-721 implementation. -/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol) -/// @dev Note that balanceOf does not revert if passed the zero address, in defiance of the ERC. -abstract contract ERC721 { - /*/////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event Transfer(address indexed from, address indexed to, uint256 indexed id); - - event Approval(address indexed owner, address indexed spender, uint256 indexed id); - - event ApprovalForAll(address indexed owner, address indexed operator, bool approved); - - /*/////////////////////////////////////////////////////////////// - METADATA STORAGE/LOGIC - //////////////////////////////////////////////////////////////*/ - - string public name; - - string public symbol; - - function tokenURI(uint256 id) public view virtual returns (string memory); - - /*/////////////////////////////////////////////////////////////// - ERC721 STORAGE - //////////////////////////////////////////////////////////////*/ - - mapping(address => uint256) public balanceOf; - - mapping(uint256 => address) public ownerOf; - - mapping(uint256 => address) public getApproved; - - mapping(address => mapping(address => bool)) public isApprovedForAll; - - /*/////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor(string memory _name, string memory _symbol) { - name = _name; - symbol = _symbol; - } - - /*/////////////////////////////////////////////////////////////// - ERC721 LOGIC - //////////////////////////////////////////////////////////////*/ - - function approve(address spender, uint256 id) public virtual { - address owner = ownerOf[id]; - - require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED"); - - getApproved[id] = spender; - - emit Approval(owner, spender, id); - } - - function setApprovalForAll(address operator, bool approved) public virtual { - isApprovedForAll[msg.sender][operator] = approved; - - emit ApprovalForAll(msg.sender, operator, approved); - } - - function transferFrom( - address from, - address to, - uint256 id - ) public virtual { - require(from == ownerOf[id], "WRONG_FROM"); - - require(to != address(0), "INVALID_RECIPIENT"); - - require( - msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id], - "NOT_AUTHORIZED" - ); - - // Underflow of the sender's balance is impossible because we check for - // ownership above and the recipient's balance can't realistically overflow. - unchecked { - balanceOf[from]--; - - balanceOf[to]++; - } - - ownerOf[id] = to; - - delete getApproved[id]; - - emit Transfer(from, to, id); - } - - function safeTransferFrom( - address from, - address to, - uint256 id - ) public virtual { - transferFrom(from, to, id); - - require( - to.code.length == 0 || - ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") == - ERC721TokenReceiver.onERC721Received.selector, - "UNSAFE_RECIPIENT" - ); - } - - function safeTransferFrom( - address from, - address to, - uint256 id, - bytes memory data - ) public virtual { - transferFrom(from, to, id); - - require( - to.code.length == 0 || - ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) == - ERC721TokenReceiver.onERC721Received.selector, - "UNSAFE_RECIPIENT" - ); - } - - /*/////////////////////////////////////////////////////////////// - ERC165 LOGIC - //////////////////////////////////////////////////////////////*/ - - function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { - return - interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 - interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721 - interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata - } - - /*/////////////////////////////////////////////////////////////// - INTERNAL MINT/BURN LOGIC - //////////////////////////////////////////////////////////////*/ - - function _mint(address to, uint256 id) internal virtual { - require(to != address(0), "INVALID_RECIPIENT"); - - require(ownerOf[id] == address(0), "ALREADY_MINTED"); - - // Counter overflow is incredibly unrealistic. - unchecked { - balanceOf[to]++; - } - - ownerOf[id] = to; - - emit Transfer(address(0), to, id); - } - - function _burn(uint256 id) internal virtual { - address owner = ownerOf[id]; - - require(owner != address(0), "NOT_MINTED"); - - // Ownership check above ensures no underflow. - unchecked { - balanceOf[owner]--; - } - - delete ownerOf[id]; - - delete getApproved[id]; - - emit Transfer(owner, address(0), id); - } - - /*/////////////////////////////////////////////////////////////// - INTERNAL SAFE MINT LOGIC - //////////////////////////////////////////////////////////////*/ - - function _safeMint(address to, uint256 id) internal virtual { - _mint(to, id); - - require( - to.code.length == 0 || - ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") == - ERC721TokenReceiver.onERC721Received.selector, - "UNSAFE_RECIPIENT" - ); - } - - function _safeMint( - address to, - uint256 id, - bytes memory data - ) internal virtual { - _mint(to, id); - - require( - to.code.length == 0 || - ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) == - ERC721TokenReceiver.onERC721Received.selector, - "UNSAFE_RECIPIENT" - ); - } -} - -/// @notice A generic interface for a contract which properly accepts ERC721 tokens. -/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol) -interface ERC721TokenReceiver { - function onERC721Received( - address operator, - address from, - uint256 id, - bytes calldata data - ) external returns (bytes4); -} diff --git a/contracts/lib/solmate/src/tokens/WETH.sol b/contracts/lib/solmate/src/tokens/WETH.sol deleted file mode 100644 index 5c470e37bd..0000000000 --- a/contracts/lib/solmate/src/tokens/WETH.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -import {ERC20} from "./ERC20.sol"; - -import {SafeTransferLib} from "../utils/SafeTransferLib.sol"; - -/// @notice Minimalist and modern Wrapped Ether implementation. -/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/WETH.sol) -/// @author Inspired by WETH9 (https://github.com/dapphub/ds-weth/blob/master/src/weth9.sol) -contract WETH is ERC20("Wrapped Ether", "WETH", 18) { - using SafeTransferLib for address; - - event Deposit(address indexed from, uint256 amount); - - event Withdrawal(address indexed to, uint256 amount); - - function deposit() public payable virtual { - _mint(msg.sender, msg.value); - - emit Deposit(msg.sender, msg.value); - } - - function withdraw(uint256 amount) public virtual { - _burn(msg.sender, amount); - - emit Withdrawal(msg.sender, amount); - - msg.sender.safeTransferETH(amount); - } - - receive() external payable virtual { - deposit(); - } -} diff --git a/contracts/lib/solmate/src/utils/Bytes32AddressLib.sol b/contracts/lib/solmate/src/utils/Bytes32AddressLib.sol deleted file mode 100644 index bc857be105..0000000000 --- a/contracts/lib/solmate/src/utils/Bytes32AddressLib.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Library for converting between addresses and bytes32 values. -/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/Bytes32AddressLib.sol) -library Bytes32AddressLib { - function fromLast20Bytes(bytes32 bytesValue) internal pure returns (address) { - return address(uint160(uint256(bytesValue))); - } - - function fillLast12Bytes(address addressValue) internal pure returns (bytes32) { - return bytes32(bytes20(addressValue)); - } -} diff --git a/contracts/lib/solmate/src/utils/CREATE3.sol b/contracts/lib/solmate/src/utils/CREATE3.sol deleted file mode 100644 index 04e091556f..0000000000 --- a/contracts/lib/solmate/src/utils/CREATE3.sol +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -import {Bytes32AddressLib} from "./Bytes32AddressLib.sol"; - -/// @notice Deploy to deterministic addresses without an initcode factor. -/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/CREATE3.sol) -/// @author Modified from 0xSequence (https://github.com/0xSequence/create3/blob/master/contracts/Create3.sol) -library CREATE3 { - using Bytes32AddressLib for bytes32; - - //--------------------------------------------------------------------------------// - // Opcode | Opcode + Arguments | Description | Stack View // - //--------------------------------------------------------------------------------// - // 0x36 | 0x36 | CALLDATASIZE | size // - // 0x3d | 0x3d | RETURNDATASIZE | 0 size // - // 0x3d | 0x3d | RETURNDATASIZE | 0 0 size // - // 0x37 | 0x37 | CALLDATACOPY | // - // 0x36 | 0x36 | CALLDATASIZE | size // - // 0x3d | 0x3d | RETURNDATASIZE | 0 size // - // 0x34 | 0x34 | CALLVALUE | value 0 size // - // 0xf0 | 0xf0 | CREATE | newContract // - //--------------------------------------------------------------------------------// - // Opcode | Opcode + Arguments | Description | Stack View // - //--------------------------------------------------------------------------------// - // 0x67 | 0x67XXXXXXXXXXXXXXXX | PUSH8 bytecode | bytecode // - // 0x3d | 0x3d | RETURNDATASIZE | 0 bytecode // - // 0x52 | 0x52 | MSTORE | // - // 0x60 | 0x6008 | PUSH1 08 | 8 // - // 0x60 | 0x6018 | PUSH1 18 | 24 8 // - // 0xf3 | 0xf3 | RETURN | // - //--------------------------------------------------------------------------------// - bytes internal constant PROXY_BYTECODE = hex"67_36_3d_3d_37_36_3d_34_f0_3d_52_60_08_60_18_f3"; - - bytes32 internal constant PROXY_BYTECODE_HASH = keccak256(PROXY_BYTECODE); - - function deploy( - bytes32 salt, - bytes memory creationCode, - uint256 value - ) internal returns (address deployed) { - bytes memory proxyChildBytecode = PROXY_BYTECODE; - - address proxy; - assembly { - // Deploy a new contract with our pre-made bytecode via CREATE2. - // We start 32 bytes into the code to avoid copying the byte length. - proxy := create2(0, add(proxyChildBytecode, 32), mload(proxyChildBytecode), salt) - } - require(proxy != address(0), "DEPLOYMENT_FAILED"); - - deployed = getDeployed(salt); - (bool success, ) = proxy.call{value: value}(creationCode); - require(success && deployed.code.length != 0, "INITIALIZATION_FAILED"); - } - - function getDeployed(bytes32 salt) internal view returns (address) { - address proxy = keccak256( - abi.encodePacked( - // Prefix: - bytes1(0xFF), - // Creator: - address(this), - // Salt: - salt, - // Bytecode hash: - PROXY_BYTECODE_HASH - ) - ).fromLast20Bytes(); - - return - keccak256( - abi.encodePacked( - // 0xd6 = 0xc0 (short RLP prefix) + 0x16 (length of: 0x94 ++ proxy ++ 0x01) - // 0x94 = 0x80 + 0x14 (0x14 = the length of an address, 20 bytes, in hex) - hex"d6_94", - proxy, - hex"01" // Nonce of the proxy contract (1) - ) - ).fromLast20Bytes(); - } -} diff --git a/contracts/lib/solmate/src/utils/FixedPointMathLib.sol b/contracts/lib/solmate/src/utils/FixedPointMathLib.sol deleted file mode 100644 index 25bb9370b5..0000000000 --- a/contracts/lib/solmate/src/utils/FixedPointMathLib.sol +++ /dev/null @@ -1,222 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Arithmetic library with operations for fixed-point numbers. -/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/FixedPointMathLib.sol) -/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol) -library FixedPointMathLib { - /*/////////////////////////////////////////////////////////////// - SIMPLIFIED FIXED POINT OPERATIONS - //////////////////////////////////////////////////////////////*/ - - uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s. - - function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) { - return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down. - } - - function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) { - return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up. - } - - function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) { - return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down. - } - - function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) { - return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up. - } - - /*/////////////////////////////////////////////////////////////// - LOW LEVEL FIXED POINT OPERATIONS - //////////////////////////////////////////////////////////////*/ - - function mulDivDown( - uint256 x, - uint256 y, - uint256 denominator - ) internal pure returns (uint256 z) { - assembly { - // Store x * y in z for now. - z := mul(x, y) - - // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y)) - if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) { - revert(0, 0) - } - - // Divide z by the denominator. - z := div(z, denominator) - } - } - - function mulDivUp( - uint256 x, - uint256 y, - uint256 denominator - ) internal pure returns (uint256 z) { - assembly { - // Store x * y in z for now. - z := mul(x, y) - - // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y)) - if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) { - revert(0, 0) - } - - // First, divide z - 1 by the denominator and add 1. - // We allow z - 1 to underflow if z is 0, because we multiply the - // end result by 0 if z is zero, ensuring we return 0 if z is zero. - z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1)) - } - } - - function rpow( - uint256 x, - uint256 n, - uint256 scalar - ) internal pure returns (uint256 z) { - assembly { - switch x - case 0 { - switch n - case 0 { - // 0 ** 0 = 1 - z := scalar - } - default { - // 0 ** n = 0 - z := 0 - } - } - default { - switch mod(n, 2) - case 0 { - // If n is even, store scalar in z for now. - z := scalar - } - default { - // If n is odd, store x in z for now. - z := x - } - - // Shifting right by 1 is like dividing by 2. - let half := shr(1, scalar) - - for { - // Shift n right by 1 before looping to halve it. - n := shr(1, n) - } n { - // Shift n right by 1 each iteration to halve it. - n := shr(1, n) - } { - // Revert immediately if x ** 2 would overflow. - // Equivalent to iszero(eq(div(xx, x), x)) here. - if shr(128, x) { - revert(0, 0) - } - - // Store x squared. - let xx := mul(x, x) - - // Round to the nearest number. - let xxRound := add(xx, half) - - // Revert if xx + half overflowed. - if lt(xxRound, xx) { - revert(0, 0) - } - - // Set x to scaled xxRound. - x := div(xxRound, scalar) - - // If n is even: - if mod(n, 2) { - // Compute z * x. - let zx := mul(z, x) - - // If z * x overflowed: - if iszero(eq(div(zx, x), z)) { - // Revert if x is non-zero. - if iszero(iszero(x)) { - revert(0, 0) - } - } - - // Round to the nearest number. - let zxRound := add(zx, half) - - // Revert if zx + half overflowed. - if lt(zxRound, zx) { - revert(0, 0) - } - - // Return properly scaled zxRound. - z := div(zxRound, scalar) - } - } - } - } - } - - /*/////////////////////////////////////////////////////////////// - GENERAL NUMBER UTILITIES - //////////////////////////////////////////////////////////////*/ - - function sqrt(uint256 x) internal pure returns (uint256 z) { - assembly { - // Start off with z at 1. - z := 1 - - // Used below to help find a nearby power of 2. - let y := x - - // Find the lowest power of 2 that is at least sqrt(x). - if iszero(lt(y, 0x100000000000000000000000000000000)) { - y := shr(128, y) // Like dividing by 2 ** 128. - z := shl(64, z) // Like multiplying by 2 ** 64. - } - if iszero(lt(y, 0x10000000000000000)) { - y := shr(64, y) // Like dividing by 2 ** 64. - z := shl(32, z) // Like multiplying by 2 ** 32. - } - if iszero(lt(y, 0x100000000)) { - y := shr(32, y) // Like dividing by 2 ** 32. - z := shl(16, z) // Like multiplying by 2 ** 16. - } - if iszero(lt(y, 0x10000)) { - y := shr(16, y) // Like dividing by 2 ** 16. - z := shl(8, z) // Like multiplying by 2 ** 8. - } - if iszero(lt(y, 0x100)) { - y := shr(8, y) // Like dividing by 2 ** 8. - z := shl(4, z) // Like multiplying by 2 ** 4. - } - if iszero(lt(y, 0x10)) { - y := shr(4, y) // Like dividing by 2 ** 4. - z := shl(2, z) // Like multiplying by 2 ** 2. - } - if iszero(lt(y, 0x8)) { - // Equivalent to 2 ** z. - z := shl(1, z) - } - - // Shifting right by 1 is like dividing by 2. - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - z := shr(1, add(z, div(x, z))) - - // Compute a rounded down version of z. - let zRoundDown := div(x, z) - - // If zRoundDown is smaller, use it. - if lt(zRoundDown, z) { - z := zRoundDown - } - } - } -} diff --git a/contracts/lib/solmate/src/utils/ReentrancyGuard.sol b/contracts/lib/solmate/src/utils/ReentrancyGuard.sol deleted file mode 100644 index 9686ca228d..0000000000 --- a/contracts/lib/solmate/src/utils/ReentrancyGuard.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Gas optimized reentrancy protection for smart contracts. -/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ReentrancyGuard.sol) -/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol) -abstract contract ReentrancyGuard { - uint256 private locked = 1; - - modifier nonReentrant() { - require(locked == 1, "REENTRANCY"); - - locked = 2; - - _; - - locked = 1; - } -} diff --git a/contracts/lib/solmate/src/utils/SSTORE2.sol b/contracts/lib/solmate/src/utils/SSTORE2.sol deleted file mode 100644 index 265f4a56ba..0000000000 --- a/contracts/lib/solmate/src/utils/SSTORE2.sol +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Read and write to persistent storage at a fraction of the cost. -/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SSTORE2.sol) -/// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol) -library SSTORE2 { - uint256 internal constant DATA_OFFSET = 1; // We skip the first byte as it's a STOP opcode to ensure the contract can't be called. - - /*/////////////////////////////////////////////////////////////// - WRITE LOGIC - //////////////////////////////////////////////////////////////*/ - - function write(bytes memory data) internal returns (address pointer) { - // Prefix the bytecode with a STOP opcode to ensure it cannot be called. - bytes memory runtimeCode = abi.encodePacked(hex"00", data); - - bytes memory creationCode = abi.encodePacked( - //---------------------------------------------------------------------------------------------------------------// - // Opcode | Opcode + Arguments | Description | Stack View // - //---------------------------------------------------------------------------------------------------------------// - // 0x60 | 0x600B | PUSH1 11 | codeOffset // - // 0x59 | 0x59 | MSIZE | 0 codeOffset // - // 0x81 | 0x81 | DUP2 | codeOffset 0 codeOffset // - // 0x38 | 0x38 | CODESIZE | codeSize codeOffset 0 codeOffset // - // 0x03 | 0x03 | SUB | (codeSize - codeOffset) 0 codeOffset // - // 0x80 | 0x80 | DUP | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset // - // 0x92 | 0x92 | SWAP3 | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) // - // 0x59 | 0x59 | MSIZE | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) // - // 0x39 | 0x39 | CODECOPY | 0 (codeSize - codeOffset) // - // 0xf3 | 0xf3 | RETURN | // - //---------------------------------------------------------------------------------------------------------------// - hex"60_0B_59_81_38_03_80_92_59_39_F3", // Returns all code in the contract except for the first 11 (0B in hex) bytes. - runtimeCode // The bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit. - ); - - assembly { - // Deploy a new contract with the generated creation code. - // We start 32 bytes into the code to avoid copying the byte length. - pointer := create(0, add(creationCode, 32), mload(creationCode)) - } - - require(pointer != address(0), "DEPLOYMENT_FAILED"); - } - - /*/////////////////////////////////////////////////////////////// - READ LOGIC - //////////////////////////////////////////////////////////////*/ - - function read(address pointer) internal view returns (bytes memory) { - return readBytecode(pointer, DATA_OFFSET, pointer.code.length - DATA_OFFSET); - } - - function read(address pointer, uint256 start) internal view returns (bytes memory) { - start += DATA_OFFSET; - - return readBytecode(pointer, start, pointer.code.length - start); - } - - function read( - address pointer, - uint256 start, - uint256 end - ) internal view returns (bytes memory) { - start += DATA_OFFSET; - end += DATA_OFFSET; - - require(pointer.code.length >= end, "OUT_OF_BOUNDS"); - - return readBytecode(pointer, start, end - start); - } - - /*/////////////////////////////////////////////////////////////// - INTERNAL HELPER LOGIC - //////////////////////////////////////////////////////////////*/ - - function readBytecode( - address pointer, - uint256 start, - uint256 size - ) private view returns (bytes memory data) { - assembly { - // Get a pointer to some free memory. - data := mload(0x40) - - // Update the free memory pointer to prevent overriding our data. - // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)). - // Adding 31 to size and running the result through the logic above ensures - // the memory pointer remains word-aligned, following the Solidity convention. - mstore(0x40, add(data, and(add(add(size, 32), 31), not(31)))) - - // Store the size of the data in the first 32 byte chunk of free memory. - mstore(data, size) - - // Copy the code into memory right after the 32 bytes we used to store the size. - extcodecopy(pointer, add(data, 32), start, size) - } - } -} diff --git a/contracts/lib/solmate/src/utils/SafeCastLib.sol b/contracts/lib/solmate/src/utils/SafeCastLib.sol deleted file mode 100644 index ab4287f729..0000000000 --- a/contracts/lib/solmate/src/utils/SafeCastLib.sol +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -/// @notice Safe unsigned integer casting library that reverts on overflow. -/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeCastLib.sol) -/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol) -library SafeCastLib { - function safeCastTo248(uint256 x) internal pure returns (uint248 y) { - require(x < 1 << 248); - - y = uint248(x); - } - - function safeCastTo224(uint256 x) internal pure returns (uint224 y) { - require(x < 1 << 224); - - y = uint224(x); - } - - function safeCastTo192(uint256 x) internal pure returns (uint192 y) { - require(x < 1 << 192); - - y = uint192(x); - } - - function safeCastTo160(uint256 x) internal pure returns (uint160 y) { - require(x < 1 << 160); - - y = uint160(x); - } - - function safeCastTo128(uint256 x) internal pure returns (uint128 y) { - require(x < 1 << 128); - - y = uint128(x); - } - - function safeCastTo96(uint256 x) internal pure returns (uint96 y) { - require(x < 1 << 96); - - y = uint96(x); - } - - function safeCastTo64(uint256 x) internal pure returns (uint64 y) { - require(x < 1 << 64); - - y = uint64(x); - } - - function safeCastTo32(uint256 x) internal pure returns (uint32 y) { - require(x < 1 << 32); - - y = uint32(x); - } - - function safeCastTo8(uint256 x) internal pure returns (uint8 y) { - require(x < 1 << 8); - - y = uint8(x); - } -} diff --git a/contracts/lib/solmate/src/utils/SafeTransferLib.sol b/contracts/lib/solmate/src/utils/SafeTransferLib.sol deleted file mode 100644 index 888376c40b..0000000000 --- a/contracts/lib/solmate/src/utils/SafeTransferLib.sol +++ /dev/null @@ -1,126 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -pragma solidity >=0.8.0; - -import {ERC20} from "../tokens/ERC20.sol"; - -/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. -/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol) -/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. -/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. -library SafeTransferLib { - event Debug(bool one, bool two, uint256 retsize); - - /*/////////////////////////////////////////////////////////////// - ETH OPERATIONS - //////////////////////////////////////////////////////////////*/ - - function safeTransferETH(address to, uint256 amount) internal { - bool success; - - assembly { - // Transfer the ETH and store if it succeeded or not. - success := call(gas(), to, amount, 0, 0, 0, 0) - } - - require(success, "ETH_TRANSFER_FAILED"); - } - - /*/////////////////////////////////////////////////////////////// - ERC20 OPERATIONS - //////////////////////////////////////////////////////////////*/ - - function safeTransferFrom( - ERC20 token, - address from, - address to, - uint256 amount - ) internal { - bool success; - - assembly { - // Get a pointer to some free memory. - let freeMemoryPointer := mload(0x40) - - // Write the abi-encoded calldata into memory, beginning with the function selector. - mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) - mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument. - mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument. - mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. - - success := and( - // Set success to whether the call reverted, if not we check it either - // returned exactly 1 (can't just be non-zero data), or had no return data. - or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), - // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. - // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. - // Counterintuitively, this call must be positioned second to the or() call in the - // surrounding and() call or else returndatasize() will be zero during the computation. - call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) - ) - } - - require(success, "TRANSFER_FROM_FAILED"); - } - - function safeTransfer( - ERC20 token, - address to, - uint256 amount - ) internal { - bool success; - - assembly { - // Get a pointer to some free memory. - let freeMemoryPointer := mload(0x40) - - // Write the abi-encoded calldata into memory, beginning with the function selector. - mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) - mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. - mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. - - success := and( - // Set success to whether the call reverted, if not we check it either - // returned exactly 1 (can't just be non-zero data), or had no return data. - or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), - // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. - // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. - // Counterintuitively, this call must be positioned second to the or() call in the - // surrounding and() call or else returndatasize() will be zero during the computation. - call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) - ) - } - - require(success, "TRANSFER_FAILED"); - } - - function safeApprove( - ERC20 token, - address to, - uint256 amount - ) internal { - bool success; - - assembly { - // Get a pointer to some free memory. - let freeMemoryPointer := mload(0x40) - - // Write the abi-encoded calldata into memory, beginning with the function selector. - mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) - mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. - mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. - - success := and( - // Set success to whether the call reverted, if not we check it either - // returned exactly 1 (can't just be non-zero data), or had no return data. - or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), - // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. - // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. - // Counterintuitively, this call must be positioned second to the or() call in the - // surrounding and() call or else returndatasize() will be zero during the computation. - call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) - ) - } - - require(success, "APPROVE_FAILED"); - } -} From 8216772d39da93ad65f72a12f0b13100230d5b42 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Thu, 31 Mar 2022 11:21:37 -0400 Subject: [PATCH 09/13] Update initializer and deploy script. --- contracts/contracts/token/WrappedOusd.sol | 6 +----- contracts/deploy/001_core.js | 4 +--- contracts/deploy/039_wrapped_ousd.js | 8 +------- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/contracts/contracts/token/WrappedOusd.sol b/contracts/contracts/token/WrappedOusd.sol index de34fcb875..a06e674b21 100644 --- a/contracts/contracts/token/WrappedOusd.sol +++ b/contracts/contracts/token/WrappedOusd.sol @@ -22,11 +22,7 @@ contract WrappedOusd is ERC4626, Governable, Initializable { /** * @notice Enable OUSD rebasing for this contract */ - function initialize(string memory name_, string memory symbol_) - external - onlyGovernor - initializer - { + function initialize() external onlyGovernor initializer { OUSD(address(asset())).rebaseOptIn(); } diff --git a/contracts/deploy/001_core.js b/contracts/deploy/001_core.js index fc17665296..cc4796a643 100644 --- a/contracts/deploy/001_core.js +++ b/contracts/deploy/001_core.js @@ -727,9 +727,7 @@ const deployWOusd = async () => { deployerAddr, [] ); - await wousd - .connect(sDeployer) - ["initialize(string,string)"]("Wrapped OUSD", "WOUSD"); + await wousd.connect(sDeployer)["initialize()"](); await wousd.connect(sDeployer).transferGovernance(governorAddr); await wousd.connect(sGovernor).claimGovernance(); }; diff --git a/contracts/deploy/039_wrapped_ousd.js b/contracts/deploy/039_wrapped_ousd.js index ada31beefa..22121b9f0a 100644 --- a/contracts/deploy/039_wrapped_ousd.js +++ b/contracts/deploy/039_wrapped_ousd.js @@ -44,13 +44,7 @@ module.exports = deploymentWithProposal( // 3. Initialize Wrapped OUSD await withConfirmation( - cWrappedOUSD - .connect(sDeployer) - ["initialize(string,string)"]( - "Wrapped OUSD", - "WOUSD", - await getTxOpts() - ) + cWrappedOUSD.connect(sDeployer)["initialize()"](await getTxOpts()) ); // 4. Assign ownership From adb4e8e94108de602351afda294077dcce6ee4d9 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Thu, 31 Mar 2022 19:19:06 -0400 Subject: [PATCH 10/13] Tweaks. --- brownie/scripts/wrapped_ousd_test.py | 2 +- contracts/deploy/039_wrapped_ousd.js | 3 --- contracts/package.json | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/brownie/scripts/wrapped_ousd_test.py b/brownie/scripts/wrapped_ousd_test.py index 3230bf7ccd..6bef606e52 100644 --- a/brownie/scripts/wrapped_ousd_test.py +++ b/brownie/scripts/wrapped_ousd_test.py @@ -2,7 +2,7 @@ import random # Designed to be used on a fork test -wrapper = load_contract('wrapped_ousd', '0x20E0c5F61124D184101a0A8d9afaeA69F5dAB907') +wrapper = load_contract('wrapped_ousd', '0x1fDb67E186C6D955f26C5d706a0F3E0aa6d49333') NUM_TESTS = 100 diff --git a/contracts/deploy/039_wrapped_ousd.js b/contracts/deploy/039_wrapped_ousd.js index 22121b9f0a..95b3c0cd72 100644 --- a/contracts/deploy/039_wrapped_ousd.js +++ b/contracts/deploy/039_wrapped_ousd.js @@ -1,8 +1,5 @@ const { deploymentWithProposal, withConfirmation } = require("../utils/deploy"); -// Deploy new staking implimentation contract with fix -// Upgrade to using it - module.exports = deploymentWithProposal( { deployName: "039_wrapped_ousd", forceDeploy: false }, async ({ deployWithConfirmation, getTxOpts, ethers }) => { diff --git a/contracts/package.json b/contracts/package.json index b1f0bf468d..d3d1f78839 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -22,7 +22,7 @@ "echidna": "yarn run clean && echidna-test . --contract PropertiesOUSDTransferable --config contracts/crytic/TestOUSDTransferable.yaml", "compute-merkle-proofs-local": "HARDHAT_NETWORK=localhost node scripts/staking/airDrop.js reimbursements.csv scripts/staking/merkleProofedAccountsToBeCompensated.json && cp scripts/staking/merkleProofedAccountsToBeCompensated.json ../dapp/src/constants/merkleProofedAccountsToBeCompensated.json", "compute-merkle-proofs-mainnet": "HARDHAT_NETWORK=mainnet node scripts/staking/airDrop.js reimbursements.csv scripts/staking/merkleProofedAccountsToBeCompensated.json && cp scripts/staking/merkleProofedAccountsToBeCompensated.json ../dapp/src/constants/merkleProofedAccountsToBeCompensated.json", - "slither": "yarn run clean && slither . --filter-paths \"crytic|mocks|solmate|@openzeppelin\" --exclude-low --exclude-informational --exclude conformance-to-solidity-naming-conventions,different-pragma-directives-are-used,external-function,assembly,incorrect-equality", + "slither": "yarn run clean && slither . --filter-paths \"crytic|mocks|openzeppelin|@openzeppelin\" --exclude-low --exclude-informational --exclude conformance-to-solidity-naming-conventions,different-pragma-directives-are-used,external-function,assembly,incorrect-equality", "clean": "rm -rf build crytic-export artifacts cache deployments/local*", "coverage": "IS_TEST=true npx hardhat coverage" }, From 066b7b7e44f8b6d1b5c3e03077f59ae51bb3f63d Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Fri, 1 Apr 2022 10:24:35 -0400 Subject: [PATCH 11/13] Test wrapper contract upgrade --- .../mocks/MockLimitedWrappedOusd.sol | 23 +++++++++++++ contracts/test/token/wousd.js | 32 ++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 contracts/contracts/mocks/MockLimitedWrappedOusd.sol diff --git a/contracts/contracts/mocks/MockLimitedWrappedOusd.sol b/contracts/contracts/mocks/MockLimitedWrappedOusd.sol new file mode 100644 index 0000000000..71ba1b55de --- /dev/null +++ b/contracts/contracts/mocks/MockLimitedWrappedOusd.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { WrappedOusd } from "../token/WrappedOusd.sol"; +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract MockLimitedWrappedOusd is WrappedOusd { + constructor( + ERC20 underlying_, + string memory name_, + string memory symbol_ + ) WrappedOusd(underlying_, name_, symbol_) {} + + function maxDeposit(address) + public + view + virtual + override + returns (uint256) + { + return 1e18; + } +} diff --git a/contracts/test/token/wousd.js b/contracts/test/token/wousd.js index 849d7135fd..8c8cc504c1 100644 --- a/contracts/test/token/wousd.js +++ b/contracts/test/token/wousd.js @@ -3,7 +3,7 @@ const { defaultFixture } = require("../_fixture"); const { ousdUnits, daiUnits, isFork, loadFixture } = require("../helpers"); -describe("WOUSD", function () { +describe.only("WOUSD", function () { if (isFork) { this.timeout(0); } @@ -93,4 +93,34 @@ describe("WOUSD", function () { ).to.be.revertedWith("Caller is not the Governor"); }); }); + + describe("WOUSD upgrade", async () => { + it("should be upgradable", async () => { + // Do upgrade + const cWrappedOUSDProxy = await ethers.getContract("WrappedOUSDProxy"); + const factory = await ethers.getContractFactory("MockLimitedWrappedOusd"); + const dNewImpl = await factory.deploy( + ousd.address, + "WOUSD", + "Wrapped OUSD" + ); + await cWrappedOUSDProxy.connect(governor).upgradeTo(dNewImpl.address); + + // Test basics + expect(await wousd.decimals()).to.eq(18); + expect(await wousd.name()).to.eq("Wrapped OUSD"); + expect(await wousd.symbol()).to.eq("WOUSD"); + + // Test previous balance + await expect(wousd).to.have.a.balanceOf("100", ousd); + await expect(josh).to.have.a.balanceOf("50", wousd); + await expect(matt).to.have.a.balanceOf("0", wousd); + + // Upgraded contract will only allow deposits of up to 1 OUSD + await wousd.connect(josh).deposit(ousdUnits("1"), josh.address); + await expect( + wousd.connect(josh).deposit(ousdUnits("25"), josh.address) + ).to.be.revertedWith("ERC4626: deposit more then max"); + }); + }); }); From 1ea253dc00b0f7bc69d05fecc2eccbd9d3b91a13 Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Fri, 1 Apr 2022 16:47:54 -0400 Subject: [PATCH 12/13] No only --- contracts/test/token/wousd.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/test/token/wousd.js b/contracts/test/token/wousd.js index 8c8cc504c1..f93bdf4e27 100644 --- a/contracts/test/token/wousd.js +++ b/contracts/test/token/wousd.js @@ -3,7 +3,7 @@ const { defaultFixture } = require("../_fixture"); const { ousdUnits, daiUnits, isFork, loadFixture } = require("../helpers"); -describe.only("WOUSD", function () { +describe("WOUSD", function () { if (isFork) { this.timeout(0); } From 3e6f597ec1f838804c781cd14ba9cccf43cbd98a Mon Sep 17 00:00:00 2001 From: Daniel Von Fange Date: Mon, 11 Apr 2022 10:46:08 -0400 Subject: [PATCH 13/13] Add hash of commit where code came from --- .../contracts/token/ERC20/extensions/ERC4626.sol | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/contracts/lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol b/contracts/lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol index 9a0692fb26..9318fd1126 100644 --- a/contracts/lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol +++ b/contracts/lib/openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol @@ -7,6 +7,10 @@ import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +// From Open Zeppelin draft PR commit: +// fac43034dca85ff539db3fc8aa2a7084b843d454 +// https://github.com/OpenZeppelin/openzeppelin-contracts/pull/3171 + abstract contract ERC4626 is ERC20, IERC4626 { IERC20Metadata private immutable _asset; @@ -172,7 +176,8 @@ abstract contract ERC4626 is ERC20, IERC4626 { return assets; } - + // Included here, since this method was not yet present in + // the version of Open Zeppelin ERC20 code we use. function _spendAllowance( address owner, address spender,